odoo-addon-l10n-br-fiscal 16.0.19.4.0__py3-none-any.whl → 17.0.4.0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. odoo/addons/l10n_br_fiscal/README.rst +10 -10
  2. odoo/addons/l10n_br_fiscal/__init__.py +2 -3
  3. odoo/addons/l10n_br_fiscal/__manifest__.py +3 -7
  4. odoo/addons/l10n_br_fiscal/constants/fiscal.py +0 -66
  5. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.cest.csv +983 -1043
  6. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.cst.csv +0 -58
  7. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.tax.csv +0 -31
  8. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.tax.group.csv +0 -3
  9. odoo/addons/l10n_br_fiscal/data/product_data.xml +2 -2
  10. odoo/addons/l10n_br_fiscal/demo/fiscal_document_demo.xml +88 -0
  11. odoo/addons/l10n_br_fiscal/i18n/l10n_br_fiscal.pot +28 -1031
  12. odoo/addons/l10n_br_fiscal/i18n/pt_BR.po +1078 -1197
  13. odoo/addons/l10n_br_fiscal/models/__init__.py +2 -2
  14. odoo/addons/l10n_br_fiscal/models/comment.py +20 -18
  15. odoo/addons/l10n_br_fiscal/models/data_abstract.py +11 -12
  16. odoo/addons/l10n_br_fiscal/models/document.py +4 -30
  17. odoo/addons/l10n_br_fiscal/models/document_line.py +3 -16
  18. odoo/addons/l10n_br_fiscal/models/document_line_mixin.py +27 -1048
  19. odoo/addons/l10n_br_fiscal/models/document_line_mixin_methods.py +883 -0
  20. odoo/addons/l10n_br_fiscal/models/document_mixin.py +3 -241
  21. odoo/addons/l10n_br_fiscal/models/document_mixin_methods.py +239 -0
  22. odoo/addons/l10n_br_fiscal/models/document_serie.py +5 -3
  23. odoo/addons/l10n_br_fiscal/models/invalidate_number.py +0 -6
  24. odoo/addons/l10n_br_fiscal/models/operation.py +0 -11
  25. odoo/addons/l10n_br_fiscal/models/operation_dashboard.py +2 -3
  26. odoo/addons/l10n_br_fiscal/models/operation_line.py +0 -28
  27. odoo/addons/l10n_br_fiscal/models/partner_profile.py +0 -6
  28. odoo/addons/l10n_br_fiscal/models/product_template.py +0 -4
  29. odoo/addons/l10n_br_fiscal/models/res_company.py +0 -18
  30. odoo/addons/l10n_br_fiscal/models/res_partner.py +0 -17
  31. odoo/addons/l10n_br_fiscal/models/simplified_tax_range.py +0 -8
  32. odoo/addons/l10n_br_fiscal/models/tax_definition.py +2 -35
  33. odoo/addons/l10n_br_fiscal/security/ir.model.access.csv +0 -6
  34. odoo/addons/l10n_br_fiscal/static/description/index.html +8 -8
  35. odoo/addons/l10n_br_fiscal/tests/__init__.py +0 -1
  36. odoo/addons/l10n_br_fiscal/tests/test_cnae.py +2 -3
  37. odoo/addons/l10n_br_fiscal/tests/test_document_edition.py +10 -37
  38. odoo/addons/l10n_br_fiscal/tests/test_fiscal_document_generic.py +28 -4
  39. odoo/addons/l10n_br_fiscal/tests/test_fiscal_document_nfse.py +4 -0
  40. odoo/addons/l10n_br_fiscal/tests/test_service_type.py +2 -3
  41. odoo/addons/l10n_br_fiscal/tests/test_tax_benefit.py +4 -2
  42. odoo/addons/l10n_br_fiscal/views/cest_view.xml +2 -2
  43. odoo/addons/l10n_br_fiscal/views/cnae_view.xml +0 -1
  44. odoo/addons/l10n_br_fiscal/views/document_line_mixin_view.xml +170 -243
  45. odoo/addons/l10n_br_fiscal/views/document_line_view.xml +7 -11
  46. odoo/addons/l10n_br_fiscal/views/document_related_view.xml +15 -11
  47. odoo/addons/l10n_br_fiscal/views/document_serie_view.xml +1 -1
  48. odoo/addons/l10n_br_fiscal/views/document_view.xml +32 -44
  49. odoo/addons/l10n_br_fiscal/views/invalidate_number_view.xml +12 -12
  50. odoo/addons/l10n_br_fiscal/views/l10n_br_fiscal_action.xml +0 -31
  51. odoo/addons/l10n_br_fiscal/views/l10n_br_fiscal_menu.xml +2 -18
  52. odoo/addons/l10n_br_fiscal/views/nbm_view.xml +2 -2
  53. odoo/addons/l10n_br_fiscal/views/nbs_view.xml +1 -1
  54. odoo/addons/l10n_br_fiscal/views/ncm_view.xml +3 -3
  55. odoo/addons/l10n_br_fiscal/views/operation_dashboard_view.xml +27 -18
  56. odoo/addons/l10n_br_fiscal/views/operation_line_view.xml +2 -5
  57. odoo/addons/l10n_br_fiscal/views/operation_view.xml +39 -16
  58. odoo/addons/l10n_br_fiscal/views/partner_profile_view.xml +3 -6
  59. odoo/addons/l10n_br_fiscal/views/product_genre_view.xml +2 -2
  60. odoo/addons/l10n_br_fiscal/views/product_product_view.xml +17 -45
  61. odoo/addons/l10n_br_fiscal/views/product_template_view.xml +16 -35
  62. odoo/addons/l10n_br_fiscal/views/res_company_view.xml +17 -18
  63. odoo/addons/l10n_br_fiscal/views/res_config_settings_view.xml +37 -98
  64. odoo/addons/l10n_br_fiscal/views/res_partner_view.xml +3 -13
  65. odoo/addons/l10n_br_fiscal/views/service_type_view.xml +2 -3
  66. odoo/addons/l10n_br_fiscal/views/tax_definition_view.xml +60 -29
  67. odoo/addons/l10n_br_fiscal/views/tax_estimate_view.xml +2 -2
  68. odoo/addons/l10n_br_fiscal/views/tax_group_view.xml +4 -4
  69. odoo/addons/l10n_br_fiscal/views/tax_view.xml +14 -16
  70. odoo/addons/l10n_br_fiscal/wizards/document_import_wizard_mixin.py +1 -1
  71. odoo/addons/l10n_br_fiscal/wizards/document_import_wizard_mixin.xml +2 -5
  72. {odoo_addon_l10n_br_fiscal-16.0.19.4.0.dist-info → odoo_addon_l10n_br_fiscal-17.0.4.0.0.1.dist-info}/METADATA +16 -16
  73. {odoo_addon_l10n_br_fiscal-16.0.19.4.0.dist-info → odoo_addon_l10n_br_fiscal-17.0.4.0.0.1.dist-info}/RECORD +77 -88
  74. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.operation.indicator.csv +0 -27
  75. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.tax.classification.csv +0 -163
  76. odoo/addons/l10n_br_fiscal/migrations/16.0.14.0.5/pre-migration.py +0 -15
  77. odoo/addons/l10n_br_fiscal/migrations/16.0.2.0.0/pre-migration.py +0 -25
  78. odoo/addons/l10n_br_fiscal/migrations/16.0.2.15.0/pre-migration.py +0 -19
  79. odoo/addons/l10n_br_fiscal/migrations/16.0.4.0.0/pre-migration.py +0 -220
  80. odoo/addons/l10n_br_fiscal/migrations/16.0.5.0.0/pre-migration.py +0 -33
  81. odoo/addons/l10n_br_fiscal/migrations/16.0.5.2.0/pre-migration.py +0 -21
  82. odoo/addons/l10n_br_fiscal/models/operation_indicator.py +0 -58
  83. odoo/addons/l10n_br_fiscal/models/tax_classification.py +0 -81
  84. odoo/addons/l10n_br_fiscal/tests/test_tax_classification.py +0 -110
  85. odoo/addons/l10n_br_fiscal/views/operation_indicator_view.xml +0 -75
  86. odoo/addons/l10n_br_fiscal/views/tax_classification.xml +0 -110
  87. /odoo/addons/l10n_br_fiscal/migrations/{16.0.13.0.0 → 17.0.2.0.0}/pre-migration.py +0 -0
  88. /odoo/addons/l10n_br_fiscal/migrations/{16.0.14.0.0 → 17.0.3.0.0}/pre-migration.py +0 -0
  89. {odoo_addon_l10n_br_fiscal-16.0.19.4.0.dist-info → odoo_addon_l10n_br_fiscal-17.0.4.0.0.1.dist-info}/WHEEL +0 -0
  90. {odoo_addon_l10n_br_fiscal-16.0.19.4.0.dist-info → odoo_addon_l10n_br_fiscal-17.0.4.0.0.1.dist-info}/top_level.txt +0 -0
@@ -8,6 +8,7 @@ from ..constants.fiscal import (
8
8
  DOCUMENT_ISSUER,
9
9
  DOCUMENT_ISSUER_COMPANY,
10
10
  FINAL_CUSTOMER,
11
+ FINAL_CUSTOMER_YES,
11
12
  FISCAL_COMMENT_DOCUMENT,
12
13
  NFE_IND_PRES,
13
14
  NFE_IND_PRES_DEFAULT,
@@ -41,6 +42,7 @@ class FiscalDocumentMixin(models.AbstractModel):
41
42
  """
42
43
 
43
44
  _name = "l10n_br_fiscal.document.mixin"
45
+ _inherit = "l10n_br_fiscal.document.mixin.methods"
44
46
  _description = "Document Fiscal Mixin Fields"
45
47
 
46
48
  def _date_server_format(self):
@@ -56,218 +58,6 @@ class FiscalDocumentMixin(models.AbstractModel):
56
58
  )
57
59
  return domain
58
60
 
59
- def _prepare_br_fiscal_dict(self, default=False):
60
- self.ensure_one()
61
- fields = self.env["l10n_br_fiscal.document.mixin"]._fields.keys()
62
-
63
- # we now read the record fiscal fields except the m2m tax:
64
- vals = self._convert_to_write(self.read(fields)[0])
65
-
66
- # remove id field to avoid conflicts
67
- vals.pop("id", None)
68
-
69
- if default: # in case you want to use new rather than write later
70
- return {f"default_{k}": vals[k] for k in vals.keys()}
71
- return vals
72
-
73
- @api.onchange("document_type_id")
74
- def _onchange_document_type_id(self):
75
- if self.document_type_id and self.issuer == DOCUMENT_ISSUER_COMPANY:
76
- self.document_serie_id = self.document_type_id.get_document_serie(
77
- self.company_id, self.fiscal_operation_id
78
- )
79
-
80
- @api.depends("fiscal_operation_id")
81
- def _compute_document_type_id(self):
82
- for doc in self.filtered(lambda doc: doc.fiscal_operation_id):
83
- if doc.issuer == DOCUMENT_ISSUER_COMPANY and not doc.document_type_id:
84
- doc.document_type_id = doc.company_id.document_type_id
85
-
86
- def _get_amount_lines(self):
87
- """Get object lines instances used to compute fiscal fields"""
88
- return self.mapped(self._get_fiscal_lines_field_name())
89
-
90
- def _get_product_amount_lines(self):
91
- fiscal_line_ids = self._get_amount_lines()
92
- return fiscal_line_ids.filtered(lambda line: line.product_id.type != "service")
93
-
94
- @api.model
95
- def _get_amount_fields(self):
96
- """Get all fields with 'amount_' prefix"""
97
- fields = self.env["l10n_br_fiscal.document.mixin"]._fields.keys()
98
- prefixes = ("amount_", "fiscal_amount_")
99
- amount_fields = [f for f in fields if f.startswith(prefixes)]
100
- return amount_fields
101
-
102
- @api.depends("document_serie_id", "issuer")
103
- def _compute_document_serie(self):
104
- for doc in self:
105
- if doc.document_serie_id and doc.issuer == DOCUMENT_ISSUER_COMPANY:
106
- doc.document_serie = doc.document_serie_id.code
107
- elif doc.document_serie is None:
108
- doc.document_serie = False
109
-
110
- @api.depends("document_type_id", "issuer")
111
- def _compute_document_serie_id(self):
112
- for doc in self:
113
- if (
114
- not doc.document_serie_id
115
- and doc.document_type_id
116
- and doc.issuer == DOCUMENT_ISSUER_COMPANY
117
- ):
118
- doc.document_serie_id = doc.document_type_id.get_document_serie(
119
- doc.company_id, doc.fiscal_operation_id
120
- )
121
- elif doc.document_serie_id is None:
122
- doc.document_serie_id = False
123
-
124
- @api.model
125
- def _get_fiscal_lines_field_name(self):
126
- return "fiscal_line_ids"
127
-
128
- def _get_fiscal_amount_field_dependencies(self):
129
- """
130
- Dynamically get the list of field dependencies.
131
- """
132
- if self._abstract:
133
- return []
134
- o2m_field_name = self._get_fiscal_lines_field_name()
135
- target_fields = []
136
- for field in self._get_amount_fields():
137
- if (
138
- field.replace("amount_", "")
139
- in getattr(self, o2m_field_name)._fields.keys()
140
- ):
141
- target_fields.append(field.replace("amount_", ""))
142
-
143
- return [o2m_field_name] + [
144
- f"{o2m_field_name}.{target_field}" for target_field in target_fields
145
- ]
146
-
147
- @api.depends(lambda self: self._get_fiscal_amount_field_dependencies())
148
- def _compute_fiscal_amount(self):
149
- """
150
- Compute and sum various fiscal amounts from the document lines.
151
-
152
- This method iterates over fields prefixed with 'amount_' (as determined
153
- by `_get_amount_fields`) and sums corresponding values from the lines
154
- retrieved by `_get_amount_lines`.
155
-
156
- It handles cases where delivery costs (freight, insurance, other) are
157
- defined at the document total level rather than per line.
158
- """
159
-
160
- fields = self._get_amount_fields()
161
- for doc in self.filtered(lambda m: m.fiscal_operation_id):
162
- values = {key: 0.0 for key in fields}
163
- for line in doc._get_amount_lines():
164
- for field in fields:
165
- if field in line._fields.keys():
166
- values[field] += line[field]
167
- if field.replace("amount_", "") in line._fields.keys():
168
- # FIXME this field creates an error in invoice form
169
- if field == "amount_financial_discount_value":
170
- values["amount_financial_discount_value"] += (
171
- 0 # line.financial_discount_value
172
- )
173
- else:
174
- values[field] += line[field.replace("amount_", "")]
175
-
176
- # Valores definidos pelo Total e não pela Linha
177
- if (
178
- doc.company_id.delivery_costs == "total"
179
- or doc.force_compute_delivery_costs_by_total
180
- ):
181
- values["amount_freight_value"] = doc.amount_freight_value
182
- values["amount_insurance_value"] = doc.amount_insurance_value
183
- values["amount_other_value"] = doc.amount_other_value
184
-
185
- doc.update(values)
186
-
187
- def _get_fiscal_partner(self):
188
- """
189
- Hook method to determine the fiscal partner for the document.
190
-
191
- This method is designed to be overridden in implementing models if the
192
- partner relevant for fiscal purposes (e.g., for tax calculations,
193
- final consumer status) is different from the main `partner_id`
194
- of the document record. For instance, an invoice might use a specific
195
- invoicing contact derived from the main partner.
196
-
197
- :return: A `res.partner` recordset representing the fiscal partner.
198
- """
199
-
200
- self.ensure_one()
201
- return self.partner_id
202
-
203
- @api.depends("partner_id")
204
- def _compute_ind_final(self):
205
- for doc in self:
206
- partner = doc._get_fiscal_partner()
207
- if partner:
208
- doc.ind_final = partner.ind_final
209
- else:
210
- # Default Value
211
- doc.ind_final = "1" # Yes
212
-
213
- @api.onchange("ind_final")
214
- def _inverse_ind_final(self):
215
- for doc in self:
216
- for line in doc._get_amount_lines():
217
- if line.ind_final != doc.ind_final:
218
- line.ind_final = doc.ind_final
219
-
220
- @api.depends("fiscal_operation_id")
221
- def _compute_operation_name(self):
222
- for doc in self:
223
- if doc.fiscal_operation_id:
224
- doc.operation_name = doc.fiscal_operation_id.name
225
- else:
226
- doc.operation_name = False
227
-
228
- @api.depends("fiscal_operation_id")
229
- def _compute_comment_ids(self):
230
- for doc in self:
231
- if doc.fiscal_operation_id:
232
- doc.comment_ids = doc.fiscal_operation_id.comment_ids
233
- elif doc.comment_ids is None:
234
- doc.comment_ids = []
235
-
236
- def _distribute_amount_to_lines(self, amount_field_name, line_field_name):
237
- for record in self:
238
- if not (
239
- record.delivery_costs == "total"
240
- or record.force_compute_delivery_costs_by_total
241
- ):
242
- continue
243
- lines = record._get_product_amount_lines()
244
- if not lines:
245
- continue
246
- amount_to_distribute = record[amount_field_name]
247
- total_gross = sum(lines.mapped("price_gross"))
248
- if total_gross > 0:
249
- distributed_amount = 0
250
- for line in lines[:-1]:
251
- proportional_amount = record.currency_id.round(
252
- amount_to_distribute * (line.price_gross / total_gross)
253
- )
254
- line[line_field_name] = proportional_amount
255
- distributed_amount += proportional_amount
256
- lines[-1][line_field_name] = amount_to_distribute - distributed_amount
257
- else:
258
- lines.write({line_field_name: 0.0})
259
- if lines:
260
- lines[0][line_field_name] = amount_to_distribute
261
-
262
- def _inverse_amount_freight(self):
263
- self._distribute_amount_to_lines("amount_freight_value", "freight_value")
264
-
265
- def _inverse_amount_insurance(self):
266
- self._distribute_amount_to_lines("amount_insurance_value", "insurance_value")
267
-
268
- def _inverse_amount_other(self):
269
- self._distribute_amount_to_lines("amount_other_value", "other_value")
270
-
271
61
  fiscal_operation_id = fields.Many2one(
272
62
  comodel_name="l10n_br_fiscal.operation",
273
63
  string="Operation",
@@ -321,11 +111,7 @@ class FiscalDocumentMixin(models.AbstractModel):
321
111
  ind_final = fields.Selection(
322
112
  selection=FINAL_CUSTOMER,
323
113
  string="Final Consumption Operation",
324
- compute="_compute_ind_final",
325
- inverse="_inverse_ind_final",
326
- store=True,
327
- precompute=True,
328
- readonly=False,
114
+ default=FINAL_CUSTOMER_YES,
329
115
  )
330
116
 
331
117
  currency_id = fields.Many2one(
@@ -345,30 +131,6 @@ class FiscalDocumentMixin(models.AbstractModel):
345
131
  store=True,
346
132
  )
347
133
 
348
- amount_ibs_base = fields.Monetary(
349
- string="IBS Base",
350
- compute="_compute_fiscal_amount",
351
- store=True,
352
- )
353
-
354
- amount_ibs_value = fields.Monetary(
355
- string="IBS Value",
356
- compute="_compute_fiscal_amount",
357
- store=True,
358
- )
359
-
360
- amount_cbs_base = fields.Monetary(
361
- string="CBS Base",
362
- compute="_compute_fiscal_amount",
363
- store=True,
364
- )
365
-
366
- amount_cbs_value = fields.Monetary(
367
- string="CBS Value",
368
- compute="_compute_fiscal_amount",
369
- store=True,
370
- )
371
-
372
134
  amount_icms_base = fields.Monetary(
373
135
  string="ICMS Base",
374
136
  compute="_compute_fiscal_amount",
@@ -0,0 +1,239 @@
1
+ # Copyright (C) 2019 Renato Lima - Akretion <renato.lima@akretion.com.br>
2
+ # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
3
+
4
+ from odoo import api, models
5
+
6
+ from ..constants.fiscal import (
7
+ DOCUMENT_ISSUER_COMPANY,
8
+ )
9
+
10
+
11
+ class FiscalDocumentMixinMethods(models.AbstractModel):
12
+ """
13
+ Provides the method implementations for l10n_br_fiscal.document.mixin.
14
+
15
+ These methods are extracted into this separate mixin due to the way
16
+ l10n_br_fiscal.document.line is incorporated into account.move
17
+ by the l10n_br_account module (decorator pattern).
18
+
19
+ Specifically:
20
+ - In l10n_br_account, fields from l10n_br_fiscal.document
21
+ are added to account.move using Odoo's `_inherits` (composition)
22
+ mechanism.
23
+ - The methods in *this* mixin, however, are intended to be inherited
24
+ using the standard `_inherit` mechanism.
25
+
26
+ This separation is crucial because `_inherits` handles field composition
27
+ but does not inherit methods. Thus, `_inherit` is used to bring in
28
+ these methods. If these methods were defined in the same class as the
29
+ fields of l10n_br_fiscal.document.mixin (which are subject to
30
+ `_inherits`), and account.move.line also used `_inherit` on that
31
+ single class, the fields would be duplicated.
32
+ """
33
+
34
+ _name = "l10n_br_fiscal.document.mixin.methods"
35
+ _description = "Fiscal Document Mixin Methods"
36
+
37
+ def _prepare_br_fiscal_dict(self, default=False):
38
+ self.ensure_one()
39
+ fields = self.env["l10n_br_fiscal.document.mixin"]._fields.keys()
40
+
41
+ # we now read the record fiscal fields except the m2m tax:
42
+ vals = self._convert_to_write(self.read(fields)[0])
43
+
44
+ # remove id field to avoid conflicts
45
+ vals.pop("id", None)
46
+
47
+ if default: # in case you want to use new rather than write later
48
+ return {f"default_{k}": vals[k] for k in vals.keys()}
49
+ return vals
50
+
51
+ @api.onchange("document_type_id")
52
+ def _onchange_document_type_id(self):
53
+ if self.document_type_id and self.issuer == DOCUMENT_ISSUER_COMPANY:
54
+ self.document_serie_id = self.document_type_id.get_document_serie(
55
+ self.company_id, self.fiscal_operation_id
56
+ )
57
+
58
+ @api.depends("fiscal_operation_id")
59
+ def _compute_document_type_id(self):
60
+ for doc in self.filtered(lambda doc: doc.fiscal_operation_id):
61
+ if doc.issuer == DOCUMENT_ISSUER_COMPANY and not doc.document_type_id:
62
+ doc.document_type_id = doc.company_id.document_type_id
63
+
64
+ def _get_amount_lines(self):
65
+ """Get object lines instances used to compute fiscal fields"""
66
+ return self.mapped(self._get_fiscal_lines_field_name())
67
+
68
+ def _get_product_amount_lines(self):
69
+ fiscal_line_ids = self._get_amount_lines()
70
+ return fiscal_line_ids.filtered(lambda line: line.product_id.type != "service")
71
+
72
+ @api.model
73
+ def _get_amount_fields(self):
74
+ """Get all fields with 'amount_' prefix"""
75
+ fields = self.env["l10n_br_fiscal.document.mixin"]._fields.keys()
76
+ prefixes = ("amount_", "fiscal_amount_")
77
+ amount_fields = [f for f in fields if f.startswith(prefixes)]
78
+ return amount_fields
79
+
80
+ @api.depends("document_serie_id", "issuer")
81
+ def _compute_document_serie(self):
82
+ for doc in self:
83
+ if doc.document_serie_id and doc.issuer == DOCUMENT_ISSUER_COMPANY:
84
+ doc.document_serie = doc.document_serie_id.code
85
+ elif doc.document_serie is None:
86
+ doc.document_serie = False
87
+
88
+ @api.depends("document_type_id", "issuer")
89
+ def _compute_document_serie_id(self):
90
+ for doc in self:
91
+ if (
92
+ not doc.document_serie_id
93
+ and doc.document_type_id
94
+ and doc.issuer == DOCUMENT_ISSUER_COMPANY
95
+ ):
96
+ doc.document_serie_id = doc.document_type_id.get_document_serie(
97
+ doc.company_id, doc.fiscal_operation_id
98
+ )
99
+ elif doc.document_serie_id is None:
100
+ doc.document_serie_id = False
101
+
102
+ @api.model
103
+ def _get_fiscal_lines_field_name(self):
104
+ return "fiscal_line_ids"
105
+
106
+ def _get_fiscal_amount_field_dependencies(self):
107
+ """
108
+ Dynamically get the list of field dependencies.
109
+ """
110
+ if self._abstract:
111
+ return []
112
+ o2m_field_name = self._get_fiscal_lines_field_name()
113
+ target_fields = []
114
+ for field in self._get_amount_fields():
115
+ if (
116
+ field.replace("amount_", "")
117
+ in getattr(self, o2m_field_name)._fields.keys()
118
+ ):
119
+ target_fields.append(field.replace("amount_", ""))
120
+
121
+ return [o2m_field_name] + [
122
+ f"{o2m_field_name}.{target_field}" for target_field in target_fields
123
+ ]
124
+
125
+ @api.depends(lambda self: self._get_fiscal_amount_field_dependencies())
126
+ def _compute_fiscal_amount(self):
127
+ """
128
+ Compute and sum various fiscal amounts from the document lines.
129
+
130
+ This method iterates over fields prefixed with 'amount_' (as determined
131
+ by `_get_amount_fields`) and sums corresponding values from the lines
132
+ retrieved by `_get_amount_lines`.
133
+
134
+ It handles cases where delivery costs (freight, insurance, other) are
135
+ defined at the document total level rather than per line.
136
+ """
137
+
138
+ fields = self._get_amount_fields()
139
+ for doc in self.filtered(lambda m: m.fiscal_operation_id):
140
+ values = {key: 0.0 for key in fields}
141
+ for line in doc._get_amount_lines():
142
+ for field in fields:
143
+ if field in line._fields.keys():
144
+ values[field] += line[field]
145
+ if field.replace("amount_", "") in line._fields.keys():
146
+ # FIXME this field creates an error in invoice form
147
+ if field == "amount_financial_discount_value":
148
+ values["amount_financial_discount_value"] += (
149
+ 0 # line.financial_discount_value
150
+ )
151
+ else:
152
+ values[field] += line[field.replace("amount_", "")]
153
+
154
+ # Valores definidos pelo Total e não pela Linha
155
+ if (
156
+ doc.company_id.delivery_costs == "total"
157
+ or doc.force_compute_delivery_costs_by_total
158
+ ):
159
+ values["amount_freight_value"] = doc.amount_freight_value
160
+ values["amount_insurance_value"] = doc.amount_insurance_value
161
+ values["amount_other_value"] = doc.amount_other_value
162
+
163
+ doc.update(values)
164
+
165
+ def _get_fiscal_partner(self):
166
+ """
167
+ Hook method to determine the fiscal partner for the document.
168
+
169
+ This method is designed to be overridden in implementing models if the
170
+ partner relevant for fiscal purposes (e.g., for tax calculations,
171
+ final consumer status) is different from the main `partner_id`
172
+ of the document record. For instance, an invoice might use a specific
173
+ invoicing contact derived from the main partner.
174
+
175
+ :return: A `res.partner` recordset representing the fiscal partner.
176
+ """
177
+
178
+ self.ensure_one()
179
+ return self.partner_id
180
+
181
+ @api.onchange("partner_id")
182
+ def _onchange_partner_id_fiscal(self):
183
+ partner = self._get_fiscal_partner()
184
+ if partner:
185
+ self.ind_final = partner.ind_final
186
+ for line in self._get_amount_lines():
187
+ # reload fiscal data, operation line, cfop, taxes, etc.
188
+ line._onchange_fiscal_operation_id()
189
+
190
+ @api.depends("fiscal_operation_id")
191
+ def _compute_operation_name(self):
192
+ for doc in self:
193
+ if doc.fiscal_operation_id:
194
+ doc.operation_name = doc.fiscal_operation_id.name
195
+ else:
196
+ doc.operation_name = False
197
+
198
+ @api.depends("fiscal_operation_id")
199
+ def _compute_comment_ids(self):
200
+ for doc in self:
201
+ if doc.fiscal_operation_id:
202
+ doc.comment_ids = doc.fiscal_operation_id.comment_ids
203
+ elif doc.comment_ids is None:
204
+ doc.comment_ids = []
205
+
206
+ def _distribute_amount_to_lines(self, amount_field_name, line_field_name):
207
+ for record in self:
208
+ if not (
209
+ record.delivery_costs == "total"
210
+ or record.force_compute_delivery_costs_by_total
211
+ ):
212
+ continue
213
+ lines = record._get_product_amount_lines()
214
+ if not lines:
215
+ continue
216
+ amount_to_distribute = record[amount_field_name]
217
+ total_gross = sum(lines.mapped("price_gross"))
218
+ if total_gross > 0:
219
+ distributed_amount = 0
220
+ for line in lines[:-1]:
221
+ proportional_amount = record.currency_id.round(
222
+ amount_to_distribute * (line.price_gross / total_gross)
223
+ )
224
+ line[line_field_name] = proportional_amount
225
+ distributed_amount += proportional_amount
226
+ lines[-1][line_field_name] = amount_to_distribute - distributed_amount
227
+ else:
228
+ lines.write({line_field_name: 0.0})
229
+ if lines:
230
+ lines[0][line_field_name] = amount_to_distribute
231
+
232
+ def _inverse_amount_freight(self):
233
+ self._distribute_amount_to_lines("amount_freight_value", "freight_value")
234
+
235
+ def _inverse_amount_insurance(self):
236
+ self._distribute_amount_to_lines("amount_insurance_value", "insurance_value")
237
+
238
+ def _inverse_amount_other(self):
239
+ self._distribute_amount_to_lines("amount_other_value", "other_value")
@@ -88,6 +88,11 @@ class DocumentSerie(models.Model):
88
88
  vals.update({"internal_sequence_id": self._create_sequence(vals)})
89
89
  return super().create(vals_list)
90
90
 
91
+ @api.depends("name")
92
+ def _compute_display_name(self):
93
+ for record in self:
94
+ record.display_name = record.name
95
+
91
96
  def write(self, vals):
92
97
  if "internal_sequence_id" in vals:
93
98
  raise ValidationError(_("You cannot change the internal sequence."))
@@ -111,9 +116,6 @@ class DocumentSerie(models.Model):
111
116
  )
112
117
  return super().write(vals)
113
118
 
114
- def name_get(self):
115
- return [(r.id, f"{r.name}") for r in self]
116
-
117
119
  def _is_invalid_number(self, document_number):
118
120
  self.ensure_one()
119
121
  is_invalid_number = True
@@ -28,14 +28,12 @@ class InvalidateNumber(models.Model):
28
28
  readonly=True,
29
29
  default=lambda self: self.env.company.id,
30
30
  required=True,
31
- states={"draft": [("readonly", False)]},
32
31
  )
33
32
 
34
33
  document_type_id = fields.Many2one(
35
34
  comodel_name="l10n_br_fiscal.document.type",
36
35
  required=True,
37
36
  readonly=True,
38
- states={"draft": [("readonly", False)]},
39
37
  )
40
38
 
41
39
  document_electronic = fields.Boolean(
@@ -49,27 +47,23 @@ class InvalidateNumber(models.Model):
49
47
  ('company_id', '=', company_id)]""",
50
48
  required=True,
51
49
  readonly=True,
52
- states={"draft": [("readonly", False)]},
53
50
  )
54
51
 
55
52
  number_start = fields.Integer(
56
53
  string="Initial Number",
57
54
  required=True,
58
55
  readonly=True,
59
- states={"draft": [("readonly", False)]},
60
56
  )
61
57
 
62
58
  number_end = fields.Integer(
63
59
  string="End Number",
64
60
  required=True,
65
61
  readonly=True,
66
- states={"draft": [("readonly", False)]},
67
62
  )
68
63
 
69
64
  justification = fields.Char(
70
65
  required=True,
71
66
  readonly=True,
72
- states={"draft": [("readonly", False)]},
73
67
  )
74
68
 
75
69
  state = fields.Selection(
@@ -72,14 +72,12 @@ class Operation(models.Model):
72
72
  code = fields.Char(
73
73
  required=True,
74
74
  readonly=True,
75
- states={"draft": [("readonly", False)]},
76
75
  tracking=True,
77
76
  )
78
77
 
79
78
  name = fields.Char(
80
79
  required=True,
81
80
  readonly=True,
82
- states={"draft": [("readonly", False)]},
83
81
  tracking=True,
84
82
  )
85
83
 
@@ -88,7 +86,6 @@ class Operation(models.Model):
88
86
  string="Type",
89
87
  required=True,
90
88
  readonly=True,
91
- states={"draft": [("readonly", False)]},
92
89
  tracking=True,
93
90
  )
94
91
 
@@ -97,7 +94,6 @@ class Operation(models.Model):
97
94
  string="Finalidade",
98
95
  default=EDOC_PURPOSE_NORMAL,
99
96
  readonly=True,
100
- states={"draft": [("readonly", False)]},
101
97
  tracking=True,
102
98
  )
103
99
 
@@ -106,7 +102,6 @@ class Operation(models.Model):
106
102
  string="Default Price Unit?",
107
103
  default="sale_price",
108
104
  readonly=True,
109
- states={"draft": [("readonly", False)]},
110
105
  tracking=True,
111
106
  )
112
107
 
@@ -115,7 +110,6 @@ class Operation(models.Model):
115
110
  default=OPERATION_FISCAL_TYPE_DEFAULT,
116
111
  required=True,
117
112
  readonly=True,
118
- states={"draft": [("readonly", False)]},
119
113
  tracking=True,
120
114
  )
121
115
 
@@ -123,7 +117,6 @@ class Operation(models.Model):
123
117
  comodel_name="l10n_br_fiscal.operation",
124
118
  string="Return Operation",
125
119
  readonly=True,
126
- states={"draft": [("readonly", False)]},
127
120
  domain="[('fiscal_operation_type', '!=', fiscal_operation_type), "
128
121
  "('fiscal_type', 'in', {'sale': ['sale_refund'], 'purchase': "
129
122
  "['purchase_refund'], 'other': ['return_in', 'return_out'],"
@@ -136,7 +129,6 @@ class Operation(models.Model):
136
129
  comodel_name="l10n_br_fiscal.operation",
137
130
  string="Inverse Operation",
138
131
  readonly=True,
139
- states={"draft": [("readonly", False)]},
140
132
  tracking=True,
141
133
  )
142
134
 
@@ -144,7 +136,6 @@ class Operation(models.Model):
144
136
  comodel_name="res.company",
145
137
  string="Company",
146
138
  readonly=True,
147
- states={"draft": [("readonly", False)]},
148
139
  tracking=True,
149
140
  )
150
141
 
@@ -162,7 +153,6 @@ class Operation(models.Model):
162
153
  inverse_name="fiscal_operation_id",
163
154
  string="Operation Document Types",
164
155
  readonly=True,
165
- states={"draft": [("readonly", False)]},
166
156
  )
167
157
 
168
158
  line_ids = fields.One2many(
@@ -170,7 +160,6 @@ class Operation(models.Model):
170
160
  inverse_name="fiscal_operation_id",
171
161
  string="Operation Line",
172
162
  readonly=True,
173
- states={"draft": [("readonly", False)]},
174
163
  copy=True,
175
164
  )
176
165
 
@@ -127,7 +127,7 @@ class Operation(models.Model):
127
127
  }
128
128
 
129
129
  def open_action(self):
130
- """Return action based on type for related journals"""
130
+ """return action based on type for related journals"""
131
131
 
132
132
  _fiscal_type_map = {
133
133
  "purchase": "in",
@@ -157,8 +157,7 @@ class Operation(models.Model):
157
157
  }
158
158
  )
159
159
 
160
- xmlid = f"l10n_br_fiscal.{action_name}"
161
- [action] = self.env.ref(xmlid).read()
160
+ [action] = self.env.ref("l10n_br_fiscal.{action_name}").read()
162
161
  action["context"] = ctx
163
162
  action["domain"] = self._context.get("use_domain", [])
164
163
  action["domain"] += [