diff --git a/addons/laundry_management/models/laundry_commission.py b/addons/laundry_management/models/laundry_commission.py new file mode 100644 index 0000000..73a9fe1 --- /dev/null +++ b/addons/laundry_management/models/laundry_commission.py @@ -0,0 +1,125 @@ +from odoo import models, fields, api +from odoo.exceptions import UserError + + +class LaundryCommission(models.Model): + """Staff commission tracking (PART 3). + + States: + pending — auto-created when order progresses; awaiting manager review + confirmed — manager has verified and approved the commission + paid — commission has been settled/paid to the staff member + + The commission_account_id (from settings) is informational for now. + Managers can bulk-confirm and bulk-mark-paid from the list view. + """ + _name = 'laundry.commission' + _description = 'Laundry Staff Commission' + _inherit = ['mail.thread'] + _order = 'date desc, id desc' + + name = fields.Char( + string='Reference', + compute='_compute_name', store=True, readonly=True, + ) + order_id = fields.Many2one( + 'sale.order', string='Order', + required=True, ondelete='cascade', index=True, + ) + company_id = fields.Many2one( + 'res.company', + related='order_id.company_id', store=True, index=True, + ) + employee_id = fields.Many2one( + 'res.users', string='Staff Member', + required=True, domain=[('share', '=', False)], + tracking=True, + ) + role = fields.Selection([ + ('reception', 'Reception / Intake'), + ('processing', 'Processing / Cleaning'), + ('delivery', 'Delivery / Handover'), + ], string='Role', required=True, tracking=True) + + commission_type = fields.Selection([ + ('percentage', 'Percentage (%)'), + ('fixed', 'Fixed Amount'), + ], string='Type', required=True, default='percentage') + + rate = fields.Float(string='Rate / Amount', digits=(10, 2)) + base_amount = fields.Float(string='Order Total', digits=(10, 2)) + commission_amount = fields.Float( + string='Commission', + compute='_compute_commission_amount', store=True, digits=(10, 2), + ) + + date = fields.Date(string='Date', required=True, default=fields.Date.today) + + state = fields.Selection([ + ('pending', 'Pending'), + ('confirmed', 'Confirmed'), + ('paid', 'Paid'), + ], default='pending', required=True, tracking=True, copy=False, index=True) + + notes = fields.Text(string='Notes') + + _ROLE_LABELS = { + 'reception': 'Reception', + 'processing': 'Processing', + 'delivery': 'Delivery', + } + + @api.depends('order_id', 'order_id.name', 'role') + def _compute_name(self): + for rec in self: + order = rec.order_id.name or 'NEW' + role = self._ROLE_LABELS.get(rec.role, rec.role or '') + rec.name = f'COM/{order}/{role}' + + @api.depends('commission_type', 'rate', 'base_amount') + def _compute_commission_amount(self): + for rec in self: + if rec.commission_type == 'percentage': + rec.commission_amount = rec.base_amount * rec.rate / 100.0 + else: + rec.commission_amount = rec.rate + + # ── State transitions ───────────────────────────────────────────── + def action_confirm(self): + """Manager confirms commission is valid and approved.""" + for rec in self: + if rec.state != 'pending': + raise UserError( + f'"{rec.name}" cannot be confirmed — current state: {rec.state}.' + ) + rec.write({'state': 'confirmed'}) + rec.message_post( + body=f'Commission confirmed by {self.env.user.name}. ' + f'Amount: {rec.commission_amount:.2f}' + ) + + def action_mark_paid(self): + """Mark commission as settled/paid to staff.""" + for rec in self: + if rec.state == 'paid': + raise UserError(f'"{rec.name}" is already paid.') + if rec.state == 'pending': + # Allow paying directly from pending (manager shortcut) + rec.write({'state': 'paid'}) + else: + rec.write({'state': 'paid'}) + rec.message_post( + body=f'Marked as paid by {self.env.user.name}.' + ) + + def action_reset_pending(self): + """Reset commission back to pending (manager only).""" + for rec in self: + if rec.state == 'paid': + raise UserError( + f'Cannot reset "{rec.name}" — it has already been paid.' + ) + rec.write({'state': 'pending'}) + rec.message_post( + body=f'Reset to pending by {self.env.user.name}.' + )