odoo-addon-l10n-br-fiscal 18.0.5.0.0.1__py3-none-any.whl → 18.0.7.1.0__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.

Potentially problematic release.


This version of odoo-addon-l10n-br-fiscal might be problematic. Click here for more details.

Files changed (54) hide show
  1. odoo/addons/l10n_br_fiscal/README.rst +1 -1
  2. odoo/addons/l10n_br_fiscal/__manifest__.py +6 -2
  3. odoo/addons/l10n_br_fiscal/constants/fiscal.py +18 -0
  4. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.cest.csv +1043 -983
  5. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.cst.csv +58 -0
  6. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.operation.indicator.csv +27 -0
  7. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.tax.classification.csv +163 -0
  8. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.tax.csv +31 -0
  9. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.tax.group.csv +3 -0
  10. odoo/addons/l10n_br_fiscal/i18n/l10n_br_fiscal.pot +523 -43
  11. odoo/addons/l10n_br_fiscal/i18n/pt_BR.po +523 -44
  12. odoo/addons/l10n_br_fiscal/models/__init__.py +2 -2
  13. odoo/addons/l10n_br_fiscal/models/data_abstract.py +9 -6
  14. odoo/addons/l10n_br_fiscal/models/document_line_mixin.py +993 -8
  15. odoo/addons/l10n_br_fiscal/models/document_mixin.py +236 -1
  16. odoo/addons/l10n_br_fiscal/models/operation_indicator.py +58 -0
  17. odoo/addons/l10n_br_fiscal/models/operation_line.py +28 -0
  18. odoo/addons/l10n_br_fiscal/models/product_template.py +4 -0
  19. odoo/addons/l10n_br_fiscal/models/res_company.py +17 -0
  20. odoo/addons/l10n_br_fiscal/models/res_partner.py +10 -0
  21. odoo/addons/l10n_br_fiscal/models/simplified_tax_range.py +8 -0
  22. odoo/addons/l10n_br_fiscal/models/tax_classification.py +81 -0
  23. odoo/addons/l10n_br_fiscal/security/ir.model.access.csv +7 -1
  24. odoo/addons/l10n_br_fiscal/static/description/index.html +1 -1
  25. odoo/addons/l10n_br_fiscal/tests/__init__.py +1 -0
  26. odoo/addons/l10n_br_fiscal/tests/test_tax_classification.py +110 -0
  27. odoo/addons/l10n_br_fiscal/views/document_line_mixin_view.xml +106 -4
  28. odoo/addons/l10n_br_fiscal/views/document_line_view.xml +4 -0
  29. odoo/addons/l10n_br_fiscal/views/document_view.xml +14 -0
  30. odoo/addons/l10n_br_fiscal/views/icms_regulation_view.xml +1 -5
  31. odoo/addons/l10n_br_fiscal/views/l10n_br_fiscal_action.xml +30 -0
  32. odoo/addons/l10n_br_fiscal/views/l10n_br_fiscal_menu.xml +16 -0
  33. odoo/addons/l10n_br_fiscal/views/nbs_view.xml +1 -5
  34. odoo/addons/l10n_br_fiscal/views/ncm_view.xml +1 -5
  35. odoo/addons/l10n_br_fiscal/views/operation_indicator_view.xml +75 -0
  36. odoo/addons/l10n_br_fiscal/views/operation_line_view.xml +4 -5
  37. odoo/addons/l10n_br_fiscal/views/operation_view.xml +1 -5
  38. odoo/addons/l10n_br_fiscal/views/product_template_view.xml +5 -0
  39. odoo/addons/l10n_br_fiscal/views/res_company_view.xml +6 -0
  40. odoo/addons/l10n_br_fiscal/views/res_partner_view.xml +4 -0
  41. odoo/addons/l10n_br_fiscal/views/service_type_view.xml +1 -5
  42. odoo/addons/l10n_br_fiscal/views/tax_classification.xml +108 -0
  43. odoo/addons/l10n_br_fiscal/views/tax_definition_view.xml +1 -5
  44. odoo/addons/l10n_br_fiscal/views/tax_view.xml +2 -2
  45. odoo/addons/l10n_br_fiscal/wizards/__init__.py +1 -1
  46. odoo/addons/l10n_br_fiscal/wizards/document_import_wizard.py +234 -0
  47. odoo/addons/l10n_br_fiscal/wizards/{document_import_wizard_mixin.xml → document_import_wizard.xml} +26 -7
  48. {odoo_addon_l10n_br_fiscal-18.0.5.0.0.1.dist-info → odoo_addon_l10n_br_fiscal-18.0.7.1.0.dist-info}/METADATA +2 -2
  49. {odoo_addon_l10n_br_fiscal-18.0.5.0.0.1.dist-info → odoo_addon_l10n_br_fiscal-18.0.7.1.0.dist-info}/RECORD +51 -46
  50. odoo/addons/l10n_br_fiscal/models/document_line_mixin_methods.py +0 -818
  51. odoo/addons/l10n_br_fiscal/models/document_mixin_methods.py +0 -247
  52. odoo/addons/l10n_br_fiscal/wizards/document_import_wizard_mixin.py +0 -129
  53. {odoo_addon_l10n_br_fiscal-18.0.5.0.0.1.dist-info → odoo_addon_l10n_br_fiscal-18.0.7.1.0.dist-info}/WHEEL +0 -0
  54. {odoo_addon_l10n_br_fiscal-18.0.5.0.0.1.dist-info → odoo_addon_l10n_br_fiscal-18.0.7.1.0.dist-info}/top_level.txt +0 -0
@@ -1,247 +0,0 @@
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.depends("partner_id")
182
- def _compute_ind_final(self):
183
- for doc in self:
184
- partner = doc._get_fiscal_partner()
185
- if partner:
186
- doc.ind_final = partner.ind_final
187
- else:
188
- # Default Value
189
- doc.ind_final = "1" # Yes
190
-
191
- @api.onchange("ind_final")
192
- def _inverse_ind_final(self):
193
- for doc in self:
194
- for line in doc._get_amount_lines():
195
- if line.ind_final != doc.ind_final:
196
- line.ind_final = doc.ind_final
197
-
198
- @api.depends("fiscal_operation_id")
199
- def _compute_operation_name(self):
200
- for doc in self:
201
- if doc.fiscal_operation_id:
202
- doc.operation_name = doc.fiscal_operation_id.name
203
- else:
204
- doc.operation_name = False
205
-
206
- @api.depends("fiscal_operation_id")
207
- def _compute_comment_ids(self):
208
- for doc in self:
209
- if doc.fiscal_operation_id:
210
- doc.comment_ids = doc.fiscal_operation_id.comment_ids
211
- elif doc.comment_ids is None:
212
- doc.comment_ids = []
213
-
214
- def _distribute_amount_to_lines(self, amount_field_name, line_field_name):
215
- for record in self:
216
- if not (
217
- record.delivery_costs == "total"
218
- or record.force_compute_delivery_costs_by_total
219
- ):
220
- continue
221
- lines = record._get_product_amount_lines()
222
- if not lines:
223
- continue
224
- amount_to_distribute = record[amount_field_name]
225
- total_gross = sum(lines.mapped("price_gross"))
226
- if total_gross > 0:
227
- distributed_amount = 0
228
- for line in lines[:-1]:
229
- proportional_amount = record.currency_id.round(
230
- amount_to_distribute * (line.price_gross / total_gross)
231
- )
232
- line[line_field_name] = proportional_amount
233
- distributed_amount += proportional_amount
234
- lines[-1][line_field_name] = amount_to_distribute - distributed_amount
235
- else:
236
- lines.write({line_field_name: 0.0})
237
- if lines:
238
- lines[0][line_field_name] = amount_to_distribute
239
-
240
- def _inverse_amount_freight(self):
241
- self._distribute_amount_to_lines("amount_freight_value", "freight_value")
242
-
243
- def _inverse_amount_insurance(self):
244
- self._distribute_amount_to_lines("amount_insurance_value", "insurance_value")
245
-
246
- def _inverse_amount_other(self):
247
- self._distribute_amount_to_lines("amount_other_value", "other_value")
@@ -1,129 +0,0 @@
1
- # Copyright (C) 2023 Felipe Zago Rodrigues - Kmee
2
- # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
3
-
4
- import base64
5
- import logging
6
-
7
- from odoo import _, api, fields, models
8
- from odoo.exceptions import UserError
9
-
10
- from ..constants.fiscal import FISCAL_IN_OUT_ALL
11
-
12
- _logger = logging.getLogger(__name__)
13
-
14
- try:
15
- from xsdata.formats.dataclass.parsers import XmlParser
16
- except ImportError:
17
- _logger.warning("xsdata Python lib not installed!")
18
-
19
-
20
- class DocumentImportWizardMixin(models.TransientModel):
21
- _name = "l10n_br_fiscal.document.import.wizard.mixin"
22
- _description = "Wizard Import Document Mixin"
23
- _inherit = "l10n_br_fiscal.base.wizard.mixin"
24
-
25
- company_id = fields.Many2one(
26
- comodel_name="res.company",
27
- string="Company",
28
- default=lambda self: self.env.company.id,
29
- )
30
-
31
- file = fields.Binary(string="File to Import")
32
-
33
- fiscal_operation_id = fields.Many2one(
34
- comodel_name="l10n_br_fiscal.operation",
35
- string="Fiscal Operation",
36
- domain="[('fiscal_operation_type', '=', fiscal_operation_type)]",
37
- )
38
-
39
- document_type = fields.Char()
40
-
41
- fiscal_operation_type = fields.Selection(selection=FISCAL_IN_OUT_ALL)
42
-
43
- def _import_edoc(self):
44
- self._find_existing_document()
45
- if not self.document_id:
46
- binding, self.document_id = self._create_edoc_from_file()
47
- else:
48
- binding = self._parse_file()
49
- return binding, self.document_id
50
-
51
- def action_import_and_open_document(self):
52
- self._import_edoc()
53
- return self.action_open_document()
54
-
55
- def _create_edoc_from_file(self):
56
- pass # meant to be overriden
57
-
58
- @api.onchange("file")
59
- def _onchange_file(self):
60
- if self.file:
61
- self._fill_wizard_from_binding()
62
-
63
- def _fill_wizard_from_binding(self):
64
- pass # meant to be overriden
65
-
66
- def action_open_document(self):
67
- return {
68
- "name": _("Document Imported"),
69
- "type": "ir.actions.act_window",
70
- "target": "current",
71
- "views": [[False, "form"]],
72
- "res_id": self.document_id.id,
73
- "res_model": "l10n_br_fiscal.document",
74
- }
75
-
76
- def _document_key_from_binding(self, binding):
77
- pass # meant to be overriden
78
-
79
- def _find_existing_document(self):
80
- document_key = self._document_key_from_binding(self._parse_file())
81
- self.document_id = self.env["l10n_br_fiscal.document"].search(
82
- [("document_key", "=", document_key.chave)],
83
- limit=1,
84
- )
85
-
86
- def _find_document_type(self, code):
87
- return self.env["l10n_br_fiscal.document.type"].search(
88
- [("code", "=", code)],
89
- limit=1,
90
- )
91
-
92
- def _find_fiscal_operation(self, cfop, nat_op, fiscal_operation_type):
93
- """try to find a matching fiscal operation via an operation line"""
94
- operation_lines = self.env["l10n_br_fiscal.operation.line"].search(
95
- [
96
- ("state", "=", "approved"),
97
- ("fiscal_type", "=", fiscal_operation_type),
98
- ("cfop_external_id", "=", cfop),
99
- ],
100
- )
101
- for line in operation_lines:
102
- if line.fiscal_operation_id.name == nat_op:
103
- return line.fiscal_operation_id
104
- if operation_lines:
105
- return operation_lines[0].fiscal_operation_id
106
-
107
- def _parse_file(self):
108
- return self._parse_file_data(self.file)
109
-
110
- @api.model
111
- def _parse_file_data(self, file_data):
112
- # NOTE: no try and a stacktrace does help for debug/support
113
- return XmlParser().from_bytes(base64.b64decode(file_data))
114
-
115
- @api.model
116
- def _detect_binding(self, binding):
117
- """
118
- A pluggable method were each specialized fiscal document
119
- importation wizard can register itself and return a tuple
120
- with (the_fiscal_document_type_code, the_name_of_the_importation_wizard)
121
- """
122
- raise UserError(
123
- _("Importation not implemented for %s!")
124
- % (
125
- type(
126
- binding,
127
- )
128
- )
129
- )