res_config_settings.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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. class ResConfigSettings(models.TransientModel):
  5. _inherit = 'res.config.settings'
  6. has_accounting_entries = fields.Boolean(compute='_compute_has_chart_of_accounts')
  7. currency_id = fields.Many2one('res.currency', related="company_id.currency_id", required=True, readonly=False,
  8. string='Currency', help="Main currency of the company.")
  9. currency_exchange_journal_id = fields.Many2one(
  10. comodel_name='account.journal',
  11. related='company_id.currency_exchange_journal_id', readonly=False,
  12. string="Currency Exchange Journal",
  13. domain="[('company_id', '=', company_id), ('type', '=', 'general')]",
  14. help='The accounting journal where automatic exchange differences will be registered')
  15. income_currency_exchange_account_id = fields.Many2one(
  16. comodel_name="account.account",
  17. related="company_id.income_currency_exchange_account_id",
  18. string="Gain Account",
  19. readonly=False,
  20. domain="[('deprecated', '=', False), ('company_id', '=', company_id),\
  21. ('account_type', 'in', ('income', 'income_other'))]")
  22. expense_currency_exchange_account_id = fields.Many2one(
  23. comodel_name="account.account",
  24. related="company_id.expense_currency_exchange_account_id",
  25. string="Loss Account",
  26. readonly=False,
  27. domain="[('deprecated', '=', False), ('company_id', '=', company_id),\
  28. ('account_type', '=', 'expense')]")
  29. has_chart_of_accounts = fields.Boolean(compute='_compute_has_chart_of_accounts', string='Company has a chart of accounts')
  30. chart_template_id = fields.Many2one('account.chart.template', string='Template', default=lambda self: self.env.company.chart_template_id,
  31. domain="[('visible','=', True)]")
  32. sale_tax_id = fields.Many2one('account.tax', string="Default Sale Tax", related='company_id.account_sale_tax_id', readonly=False)
  33. purchase_tax_id = fields.Many2one('account.tax', string="Default Purchase Tax", related='company_id.account_purchase_tax_id', readonly=False)
  34. tax_calculation_rounding_method = fields.Selection(
  35. related='company_id.tax_calculation_rounding_method', string='Tax calculation rounding method', readonly=False)
  36. account_journal_suspense_account_id = fields.Many2one(
  37. comodel_name='account.account',
  38. string='Bank Suspense Account',
  39. readonly=False,
  40. related='company_id.account_journal_suspense_account_id',
  41. domain="[('deprecated', '=', False), ('company_id', '=', company_id), ('account_type', 'in', ('asset_current', 'liability_current'))]",
  42. help='Bank Transactions are posted immediately after import or synchronization. '
  43. 'Their counterparty is the bank suspense account.\n'
  44. 'Reconciliation replaces the latter by the definitive account(s).')
  45. account_journal_payment_debit_account_id = fields.Many2one(
  46. comodel_name='account.account',
  47. string='Outstanding Receipts Account',
  48. readonly=False,
  49. related='company_id.account_journal_payment_debit_account_id',
  50. domain="[('deprecated', '=', False), ('company_id', '=', company_id), ('account_type', '=', 'asset_current')]",
  51. help='Incoming payments are posted on an Outstanding Receipts Account. '
  52. 'In the bank reconciliation widget, they appear as blue lines.\n'
  53. 'Bank transactions are then reconciled on the Outstanding Receipts Accounts rather than the Receivable '
  54. 'Account.')
  55. account_journal_payment_credit_account_id = fields.Many2one(
  56. comodel_name='account.account',
  57. string='Outstanding Payments Account',
  58. readonly=False,
  59. related='company_id.account_journal_payment_credit_account_id',
  60. domain="[('deprecated', '=', False), ('company_id', '=', company_id), ('account_type', '=', 'asset_current')]",
  61. help='Outgoing Payments are posted on an Outstanding Payments Account. '
  62. 'In the bank reconciliation widget, they appear as blue lines.\n'
  63. 'Bank transactions are then reconciled on the Outstanding Payments Account rather the Payable Account.')
  64. transfer_account_id = fields.Many2one('account.account', string="Internal Transfer Account",
  65. related='company_id.transfer_account_id', readonly=False,
  66. domain=[
  67. ('reconcile', '=', True),
  68. ('account_type', '=', 'asset_current'),
  69. ('deprecated', '=', False),
  70. ],
  71. help="Intermediary account used when moving from a liquidity account to another.")
  72. module_account_accountant = fields.Boolean(string='Accounting')
  73. group_warning_account = fields.Boolean(string="Warnings in Invoices", implied_group='account.group_warning_account')
  74. group_cash_rounding = fields.Boolean(string="Cash Rounding", implied_group='account.group_cash_rounding')
  75. # group_show_line_subtotals_tax_excluded and group_show_line_subtotals_tax_included are opposite,
  76. # so we can assume exactly one of them will be set, and not the other.
  77. # We need both of them to coexist so we can take advantage of automatic group assignation.
  78. group_show_line_subtotals_tax_excluded = fields.Boolean(
  79. "Show line subtotals without taxes (B2B)",
  80. implied_group='account.group_show_line_subtotals_tax_excluded',
  81. group='base.group_portal,base.group_user,base.group_public',
  82. compute='_compute_group_show_line_subtotals', store=True, readonly=False)
  83. group_show_line_subtotals_tax_included = fields.Boolean(
  84. "Show line subtotals with taxes (B2C)",
  85. implied_group='account.group_show_line_subtotals_tax_included',
  86. group='base.group_portal,base.group_user,base.group_public',
  87. compute='_compute_group_show_line_subtotals', store=True, readonly=False)
  88. group_show_sale_receipts = fields.Boolean(string='Sale Receipt',
  89. implied_group='account.group_sale_receipts')
  90. group_show_purchase_receipts = fields.Boolean(string='Purchase Receipt',
  91. implied_group='account.group_purchase_receipts')
  92. show_line_subtotals_tax_selection = fields.Selection([
  93. ('tax_excluded', 'Tax Excluded'),
  94. ('tax_included', 'Tax Included')], string="Line Subtotals Tax Display",
  95. required=True, default='tax_excluded',
  96. config_parameter='account.show_line_subtotals_tax_selection')
  97. module_account_budget = fields.Boolean(string='Budget Management')
  98. module_account_payment = fields.Boolean(string='Invoice Online Payment')
  99. module_account_reports = fields.Boolean("Dynamic Reports")
  100. module_account_check_printing = fields.Boolean("Allow check printing and deposits")
  101. module_account_batch_payment = fields.Boolean(string='Use batch payments',
  102. help='This allows you grouping payments into a single batch and eases the reconciliation process.\n'
  103. '-This installs the account_batch_payment module.')
  104. module_account_sepa = fields.Boolean(string='SEPA Credit Transfer (SCT)')
  105. module_account_sepa_direct_debit = fields.Boolean(string='Use SEPA Direct Debit')
  106. module_account_bank_statement_import_qif = fields.Boolean("Import .qif files")
  107. module_account_bank_statement_import_ofx = fields.Boolean("Import in .ofx format")
  108. module_account_bank_statement_import_csv = fields.Boolean("Import in .csv format")
  109. module_account_bank_statement_import_camt = fields.Boolean("Import in CAMT.053 format")
  110. module_currency_rate_live = fields.Boolean(string="Automatic Currency Rates")
  111. module_account_intrastat = fields.Boolean(string='Intrastat')
  112. module_product_margin = fields.Boolean(string="Allow Product Margin")
  113. module_l10n_eu_oss = fields.Boolean(string="EU Intra-community Distance Selling")
  114. module_account_taxcloud = fields.Boolean(string="Account TaxCloud")
  115. module_account_invoice_extract = fields.Boolean(string="Document Digitization")
  116. module_snailmail_account = fields.Boolean(string="Snailmail")
  117. tax_exigibility = fields.Boolean(string='Cash Basis', related='company_id.tax_exigibility', readonly=False)
  118. tax_cash_basis_journal_id = fields.Many2one('account.journal', related='company_id.tax_cash_basis_journal_id', string="Tax Cash Basis Journal", readonly=False)
  119. account_cash_basis_base_account_id = fields.Many2one(
  120. comodel_name='account.account',
  121. string="Base Tax Received Account",
  122. readonly=False,
  123. related='company_id.account_cash_basis_base_account_id',
  124. domain=[('deprecated', '=', False)])
  125. account_fiscal_country_id = fields.Many2one(string="Fiscal Country Code", related="company_id.account_fiscal_country_id", readonly=False, store=False)
  126. qr_code = fields.Boolean(string='Display SEPA QR-code', related='company_id.qr_code', readonly=False)
  127. invoice_is_print = fields.Boolean(string='Print', related='company_id.invoice_is_print', readonly=False)
  128. invoice_is_email = fields.Boolean(string='Send Email', related='company_id.invoice_is_email', readonly=False)
  129. incoterm_id = fields.Many2one('account.incoterms', string='Default incoterm', related='company_id.incoterm_id', help='International Commercial Terms are a series of predefined commercial terms used in international transactions.', readonly=False)
  130. invoice_terms = fields.Html(related='company_id.invoice_terms', string="Terms & Conditions", readonly=False)
  131. invoice_terms_html = fields.Html(related='company_id.invoice_terms_html', string="Terms & Conditions as a Web page",
  132. readonly=False)
  133. terms_type = fields.Selection(
  134. related='company_id.terms_type', readonly=False)
  135. preview_ready = fields.Boolean(string="Display preview button", compute='_compute_terms_preview')
  136. use_invoice_terms = fields.Boolean(
  137. string='Default Terms & Conditions',
  138. config_parameter='account.use_invoice_terms')
  139. account_use_credit_limit = fields.Boolean(
  140. string="Sales Credit Limit", related="company_id.account_use_credit_limit", readonly=False,
  141. help="Enable the use of credit limit on partners.")
  142. account_default_credit_limit = fields.Monetary(
  143. string="Default Credit Limit", readonly=False,
  144. help='This is the default credit limit that will be used on partners that do not have a specific limit on them.',
  145. compute="_compute_account_default_credit_limit", inverse="_inverse_account_default_credit_limit")
  146. # Technical field to hide country specific fields from accounting configuration
  147. country_code = fields.Char(related='company_id.account_fiscal_country_id.code', readonly=True)
  148. # Storno Accounting
  149. account_storno = fields.Boolean(string="Storno accounting", readonly=False, related='company_id.account_storno')
  150. # Allows for the use of a different delivery address
  151. group_sale_delivery_address = fields.Boolean("Customer Addresses", implied_group='account.group_delivery_invoice_address')
  152. # Quick encoding (fiduciary mode)
  153. quick_edit_mode = fields.Selection(string="Quick encoding", readonly=False, related='company_id.quick_edit_mode')
  154. early_pay_discount_computation = fields.Selection(related='company_id.early_pay_discount_computation', string='Tax setting', readonly=False)
  155. account_journal_early_pay_discount_loss_account_id = fields.Many2one(
  156. comodel_name='account.account',
  157. string='Cash Discount Loss account',
  158. help='Account for the difference amount after the expense discount has been granted',
  159. readonly=False,
  160. related='company_id.account_journal_early_pay_discount_loss_account_id',
  161. domain="[('deprecated', '=', False), ('company_id', '=', company_id), ('account_type', 'in', ('expense', 'income', 'income_other'))]",
  162. )
  163. account_journal_early_pay_discount_gain_account_id = fields.Many2one(
  164. comodel_name='account.account',
  165. string='Cash Discount Gain account',
  166. help='Account for the difference amount after the income discount has been granted',
  167. readonly=False,
  168. related='company_id.account_journal_early_pay_discount_gain_account_id',
  169. domain="[('deprecated', '=', False), ('company_id', '=', company_id), ('account_type', 'in', ('income', 'income_other', 'expense'))]",
  170. )
  171. def set_values(self):
  172. super().set_values()
  173. # install a chart of accounts for the given company (if required)
  174. if self.env.company == self.company_id \
  175. and self.chart_template_id \
  176. and self.chart_template_id != self.company_id.chart_template_id:
  177. self.chart_template_id._load(self.env.company)
  178. @api.depends('company_id')
  179. def _compute_account_default_credit_limit(self):
  180. for setting in self:
  181. setting.account_default_credit_limit = self.env['ir.property']._get('credit_limit', 'res.partner')
  182. def _inverse_account_default_credit_limit(self):
  183. for setting in self:
  184. self.env['ir.property']._set_default(
  185. 'credit_limit',
  186. 'res.partner',
  187. setting.account_default_credit_limit,
  188. self.company_id.id
  189. )
  190. @api.depends('company_id')
  191. def _compute_has_chart_of_accounts(self):
  192. self.has_chart_of_accounts = bool(self.company_id.chart_template_id)
  193. self.has_accounting_entries = self.env['account.chart.template'].existing_accounting(self.company_id)
  194. @api.depends('show_line_subtotals_tax_selection')
  195. def _compute_group_show_line_subtotals(self):
  196. for wizard in self:
  197. wizard.group_show_line_subtotals_tax_included = wizard.show_line_subtotals_tax_selection == "tax_included"
  198. wizard.group_show_line_subtotals_tax_excluded = wizard.show_line_subtotals_tax_selection == "tax_excluded"
  199. @api.onchange('group_analytic_accounting')
  200. def onchange_analytic_accounting(self):
  201. if self.group_analytic_accounting:
  202. self.module_account_accountant = True
  203. @api.onchange('module_account_budget')
  204. def onchange_module_account_budget(self):
  205. if self.module_account_budget:
  206. self.group_analytic_accounting = True
  207. @api.onchange('tax_exigibility')
  208. def _onchange_tax_exigibility(self):
  209. res = {}
  210. tax = self.env['account.tax'].search([
  211. ('company_id', '=', self.env.company.id), ('tax_exigibility', '=', 'on_payment')
  212. ], limit=1)
  213. if not self.tax_exigibility and tax:
  214. self.tax_exigibility = True
  215. res['warning'] = {
  216. 'title': _('Error!'),
  217. 'message': _('You cannot disable this setting because some of your taxes are cash basis. '
  218. 'Modify your taxes first before disabling this setting.')
  219. }
  220. return res
  221. @api.depends('terms_type')
  222. def _compute_terms_preview(self):
  223. for setting in self:
  224. # We display the preview button only if the terms_type is html in the setting but also on the company
  225. # to avoid landing on an error page (see terms.py controller)
  226. setting.preview_ready = self.env.company.terms_type == 'html' and setting.terms_type == 'html'
  227. def action_update_terms(self):
  228. self.ensure_one()
  229. if hasattr(self, 'website_id') and self.env.user.has_group('website.group_website_designer'):
  230. return self.env["website"].get_client_action('/terms', True)
  231. return {
  232. 'name': _('Update Terms & Conditions'),
  233. 'type': 'ir.actions.act_window',
  234. 'view_mode': 'form',
  235. 'res_model': 'res.company',
  236. 'view_id': self.env.ref("account.res_company_view_form_terms", False).id,
  237. 'target': 'new',
  238. 'res_id': self.company_id.id,
  239. }