117 lines
5.1 KiB
Python
117 lines
5.1 KiB
Python
from odoo import api, fields, models, _
|
|
from odoo.exceptions import ValidationError
|
|
from datetime import timedelta
|
|
|
|
|
|
class EducationLibraryIssue(models.Model):
|
|
_name = 'education.library.issue'
|
|
_description = 'Library Book Issue'
|
|
_rec_name = 'name'
|
|
_order = 'issue_date desc'
|
|
|
|
name = fields.Char(string='Reference', required=True, copy=False, readonly=True, default=lambda self: _('New'))
|
|
member_id = fields.Many2one('education.library.member', string='Member', required=True)
|
|
copy_id = fields.Many2one('education.library.book.copy', string='Book Copy', required=True)
|
|
library_id = fields.Many2one('education.library', string='Library', related='copy_id.library_id', store=True)
|
|
|
|
issue_date = fields.Date(string='Issue Date', default=fields.Date.context_today, required=True)
|
|
due_date = fields.Date(string='Due Date', compute='_compute_due_date', store=True, readonly=False)
|
|
return_date = fields.Date(string='Return Date')
|
|
|
|
status = fields.Selection([
|
|
('draft', 'Draft'),
|
|
('issued', 'Issued'),
|
|
('returned', 'Returned'),
|
|
('overdue', 'Overdue'),
|
|
('cancelled', 'Cancelled')
|
|
], string='Status', default='draft', required=True)
|
|
|
|
remarks = fields.Text(string='Remarks')
|
|
fine_amount = fields.Float(string='Fine Amount', compute='_compute_fine_amount')
|
|
fine_ids = fields.One2many('education.library.fine', 'issue_id', string='Fines')
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
for vals in vals_list:
|
|
if vals.get('name', _('New')) == _('New'):
|
|
vals['name'] = self.env['ir.sequence'].sudo().next_by_code('education.library.issue') or _('New')
|
|
return super().create(vals_list)
|
|
|
|
@api.depends('issue_date')
|
|
def _compute_due_date(self):
|
|
loan_days = int(self.env['ir.config_parameter'].sudo().get_param('education_library.loan_period_days', 14))
|
|
for record in self:
|
|
if record.issue_date and not record.due_date:
|
|
record.due_date = record.issue_date + timedelta(days=loan_days)
|
|
|
|
@api.depends('fine_ids', 'fine_ids.amount')
|
|
def _compute_fine_amount(self):
|
|
for record in self:
|
|
record.fine_amount = sum(record.fine_ids.mapped('amount'))
|
|
|
|
@api.constrains('issue_date', 'due_date')
|
|
def _check_dates(self):
|
|
for record in self:
|
|
if record.due_date < record.issue_date:
|
|
raise ValidationError(_('Due date must be greater than or equal to issue date.'))
|
|
|
|
def action_issue(self):
|
|
for record in self:
|
|
if record.copy_id.status != 'available':
|
|
raise ValidationError(_('The selected book copy is not available for issue.'))
|
|
if not record.member_id.active:
|
|
raise ValidationError(_('Active member required.'))
|
|
if record.member_id.membership_end and record.member_id.membership_end < fields.Date.today():
|
|
raise ValidationError(_('Membership dates do not permit issue.'))
|
|
|
|
# Check issue limit
|
|
active_issues = self.search_count([
|
|
('member_id', '=', record.member_id.id),
|
|
('status', 'in', ['issued', 'overdue'])
|
|
])
|
|
if active_issues >= record.member_id.issue_limit:
|
|
raise ValidationError(_('Issue limit enforced: Member has reached their maximum active issues limit.'))
|
|
|
|
record.copy_id.status = 'issued'
|
|
record.status = 'issued'
|
|
|
|
def action_return(self):
|
|
for record in self:
|
|
if record.status not in ['issued', 'overdue']:
|
|
raise ValidationError(_('Only issued or overdue records can be returned.'))
|
|
|
|
return_date = fields.Date.today()
|
|
record.return_date = return_date
|
|
|
|
# Calculate fine if late
|
|
days_late = max((return_date - record.due_date).days, 0)
|
|
if days_late > 0:
|
|
fine_per_day = float(self.env['ir.config_parameter'].sudo().get_param('education_library.fine_per_day', 10.0))
|
|
fine_amt = days_late * fine_per_day
|
|
if fine_amt > 0:
|
|
self.env['education.library.fine'].create({
|
|
'issue_id': record.id,
|
|
'member_id': record.member_id.id,
|
|
'student_id': record.member_id.student_id.id if record.member_id.student_id else False,
|
|
'days_late': days_late,
|
|
'amount': fine_amt,
|
|
'state': 'draft'
|
|
})
|
|
|
|
record.copy_id.status = 'available'
|
|
record.status = 'returned'
|
|
|
|
def action_cancel(self):
|
|
for record in self:
|
|
if record.status not in ['draft']:
|
|
raise ValidationError(_('Only draft issues can be cancelled.'))
|
|
record.status = 'cancelled'
|
|
|
|
@api.model
|
|
def _cron_mark_overdue(self):
|
|
overdue_issues = self.search([
|
|
('status', '=', 'issued'),
|
|
('due_date', '<', fields.Date.today())
|
|
])
|
|
overdue_issues.write({'status': 'overdue'})
|