102 lines
4.5 KiB
Python
102 lines
4.5 KiB
Python
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)
|
|
)
|