account_payment_method.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. from odoo import api, fields, models, _
  4. from odoo.osv import expression
  5. from odoo.exceptions import UserError
  6. class AccountPaymentMethod(models.Model):
  7. _name = "account.payment.method"
  8. _description = "Payment Methods"
  9. name = fields.Char(required=True, translate=True)
  10. code = fields.Char(required=True) # For internal identification
  11. payment_type = fields.Selection(selection=[('inbound', 'Inbound'), ('outbound', 'Outbound')], required=True)
  12. _sql_constraints = [
  13. ('name_code_unique', 'unique (code, payment_type)', 'The combination code/payment type already exists!'),
  14. ]
  15. @api.model_create_multi
  16. def create(self, vals_list):
  17. payment_methods = super().create(vals_list)
  18. methods_info = self._get_payment_method_information()
  19. for method in payment_methods:
  20. information = methods_info.get(method.code, {})
  21. if information.get('mode') == 'multi':
  22. method_domain = method._get_payment_method_domain(method.code)
  23. journals = self.env['account.journal'].search(method_domain)
  24. self.env['account.payment.method.line'].create([{
  25. 'name': method.name,
  26. 'payment_method_id': method.id,
  27. 'journal_id': journal.id
  28. } for journal in journals])
  29. return payment_methods
  30. @api.model
  31. def _get_payment_method_domain(self, code):
  32. """
  33. :return: The domain specyfying which journal can accomodate this payment method.
  34. """
  35. if not code:
  36. return []
  37. information = self._get_payment_method_information().get(code)
  38. currency_ids = information.get('currency_ids')
  39. country_id = information.get('country_id')
  40. default_domain = [('type', 'in', ('bank', 'cash'))]
  41. domains = [information.get('domain', default_domain)]
  42. if currency_ids:
  43. domains += [expression.OR([
  44. [('currency_id', '=', False), ('company_id.currency_id', 'in', currency_ids)],
  45. [('currency_id', 'in', currency_ids)]],
  46. )]
  47. if country_id:
  48. domains += [[('company_id.account_fiscal_country_id', '=', country_id)]]
  49. return expression.AND(domains)
  50. @api.model
  51. def _get_payment_method_information(self):
  52. """
  53. Contains details about how to initialize a payment method with the code x.
  54. The contained info are:
  55. mode: Either unique if we only want one of them at a single time (payment providers for example)
  56. or multi if we want the method on each journal fitting the domain.
  57. domain: The domain defining the eligible journals.
  58. currency_id: The id of the currency necessary on the journal (or company) for it to be eligible.
  59. country_id: The id of the country needed on the company for it to be eligible.
  60. hidden: If set to true, the method will not be automatically added to the journal,
  61. and will not be selectable by the user.
  62. """
  63. return {
  64. 'manual': {'mode': 'multi', 'domain': [('type', 'in', ('bank', 'cash'))]},
  65. }
  66. @api.model
  67. def _get_sdd_payment_method_code(self):
  68. """
  69. TO OVERRIDE
  70. This hook will be used to return the list of sdd payment method codes
  71. """
  72. return []
  73. class AccountPaymentMethodLine(models.Model):
  74. _name = "account.payment.method.line"
  75. _description = "Payment Methods"
  76. _order = 'sequence, id'
  77. # == Business fields ==
  78. name = fields.Char(compute='_compute_name', readonly=False, store=True)
  79. sequence = fields.Integer(default=10)
  80. payment_method_id = fields.Many2one(
  81. string='Payment Method',
  82. comodel_name='account.payment.method',
  83. domain="[('payment_type', '=?', payment_type), ('id', 'in', available_payment_method_ids)]",
  84. required=True,
  85. ondelete='cascade'
  86. )
  87. payment_account_id = fields.Many2one(
  88. comodel_name='account.account',
  89. check_company=True,
  90. copy=False,
  91. ondelete='restrict',
  92. domain="[('deprecated', '=', False), "
  93. "('company_id', '=', company_id), "
  94. "('account_type', 'not in', ('asset_receivable', 'liability_payable')), "
  95. "'|', ('account_type', 'in', ('asset_current', 'liability_current')), ('id', '=', parent.default_account_id)]"
  96. )
  97. journal_id = fields.Many2one(comodel_name='account.journal', ondelete="cascade")
  98. # == Display purpose fields ==
  99. code = fields.Char(related='payment_method_id.code')
  100. payment_type = fields.Selection(related='payment_method_id.payment_type')
  101. company_id = fields.Many2one(related='journal_id.company_id')
  102. available_payment_method_ids = fields.Many2many(related='journal_id.available_payment_method_ids')
  103. @api.depends('payment_method_id.name')
  104. def _compute_name(self):
  105. for method in self:
  106. if not method.name:
  107. method.name = method.payment_method_id.name
  108. @api.constrains('name')
  109. def _ensure_unique_name_for_journal(self):
  110. self.flush_model(['name', 'journal_id', 'payment_method_id'])
  111. self.env['account.payment.method'].flush_model(['payment_type'])
  112. self._cr.execute('''
  113. SELECT apml.name, apm.payment_type
  114. FROM account_payment_method_line apml
  115. JOIN account_payment_method apm ON apml.payment_method_id = apm.id
  116. WHERE apml.journal_id IS NOT NULL
  117. GROUP BY apml.name, journal_id, apm.payment_type
  118. HAVING count(apml.id) > 1
  119. ''')
  120. res = self._cr.fetchall()
  121. if res:
  122. (name, payment_type) = res[0]
  123. raise UserError(_("You can't have two payment method lines of the same payment type (%s) "
  124. "and with the same name (%s) on a single journal.", payment_type, name))
  125. def unlink(self):
  126. """
  127. Payment method lines which are used in a payment should not be deleted from the database,
  128. only the link betweend them and the journals must be broken.
  129. """
  130. unused_payment_method_lines = self
  131. for line in self:
  132. payment_count = self.env['account.payment'].sudo().search_count([('payment_method_line_id', '=', line.id)])
  133. if payment_count > 0:
  134. unused_payment_method_lines -= line
  135. (self - unused_payment_method_lines).write({'journal_id': False})
  136. return super(AccountPaymentMethodLine, unused_payment_method_lines).unlink()
  137. @api.model
  138. def _auto_toggle_account_to_reconcile(self, account_id):
  139. """ Automatically toggle the account to reconcile if allowed.
  140. :param account_id: The id of an account.account.
  141. """
  142. account = self.env['account.account'].browse(account_id)
  143. if not account.reconcile and account.account_type not in ('asset_cash', 'liability_credit_card') and account.internal_group != 'off_balance':
  144. account.reconcile = True
  145. @api.model_create_multi
  146. def create(self, vals_list):
  147. # OVERRIDE
  148. for vals in vals_list:
  149. if vals.get('payment_account_id'):
  150. self._auto_toggle_account_to_reconcile(vals['payment_account_id'])
  151. return super().create(vals_list)
  152. def write(self, vals):
  153. # OVERRIDE
  154. if vals.get('payment_account_id'):
  155. self._auto_toggle_account_to_reconcile(vals['payment_account_id'])
  156. return super().write(vals)