from odoo import models, fields, api from odoo.exceptions import UserError class LaundryPaymentMethod(models.Model): """Configurable payment method linked to an accounting journal. One record per payment option (e.g. Cash, Visa, Bank Transfer, Credit). Each method is typed as cash / bank / credit so that session cash-control and accounting routing work exactly like Odoo POS payment methods. cash → counted in the session cash drawer bank → posted to a bank/card journal, not counted in cash credit → deferred / no-posting (customer owes) """ _name = 'laundry.payment.method' _description = 'Laundry Payment Method' _order = 'sequence, id' # ── Identity ────────────────────────────────────────────────────── name = fields.Char( string='Method Name', required=True, translate=True, ) sequence = fields.Integer(default=10) active = fields.Boolean(default=True) company_id = fields.Many2one( 'res.company', default=lambda self: self.env.company, ) # ── Type ────────────────────────────────────────────────────────── payment_type = fields.Selection([ ('cash', 'Cash / نقد'), ('bank', 'Bank / Card / بنك'), ('credit', 'Credit / Deferred / آجل'), ], string='Type', required=True, default='cash', help=( 'cash → counts in session cash drawer\n' 'bank → posted to bank/card journal\n' 'credit → deferred, no immediate accounting entry' ), ) # ── Journal ─────────────────────────────────────────────────────── journal_id = fields.Many2one( 'account.journal', string='Accounting Journal', domain="[('type', 'in', ['cash', 'bank']), ('company_id', '=', company_id)]", help='Leave blank only for Credit/Deferred methods.', ) # ── UI ──────────────────────────────────────────────────────────── is_default = fields.Boolean( string='Default', help='Pre-selected when staff opens the Register Payment wizard.', ) # ── Constraints ─────────────────────────────────────────────────── @api.constrains('is_default', 'company_id') def _check_single_default(self): for rec in self.filtered('is_default'): duplicate = self.search([ ('is_default', '=', True), ('company_id', '=', rec.company_id.id), ('id', '!=', rec.id), ], limit=1) if duplicate: raise UserError( f'Only one default payment method is allowed per company.\n' f'"{duplicate.name}" is already the default.\n' 'Unset it first before marking this one as default.' ) @api.constrains('payment_type', 'journal_id') def _check_journal_required(self): for rec in self: if rec.payment_type in ('cash', 'bank') and not rec.journal_id: raise UserError( f'Payment method "{rec.name}" is of type "{rec.payment_type}" ' 'and requires an accounting journal. ' 'Please select a journal or change the type to Credit/Deferred.' ) # ── Helpers ─────────────────────────────────────────────────────── @api.model def get_default_method(self): """Return the default payment method for the current company.""" company = self.env.company return ( self.search([ ('is_default', '=', True), ('company_id', '=', company.id), ('active', '=', True), ], limit=1) or self.search([ ('payment_type', '=', 'cash'), ('company_id', '=', company.id), ('active', '=', True), ], limit=1) )