odoo-addon-l10n-br-fiscal 18.0.2.0.0.11__py3-none-any.whl → 18.0.3.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.
- odoo/addons/l10n_br_fiscal/README.rst +1 -1
- odoo/addons/l10n_br_fiscal/__manifest__.py +1 -1
- odoo/addons/l10n_br_fiscal/demo/fiscal_document_demo.xml +2 -90
- odoo/addons/l10n_br_fiscal/demo/fiscal_document_nfse_demo.xml +0 -4
- odoo/addons/l10n_br_fiscal/demo/fiscal_operation_demo.xml +2 -2
- odoo/addons/l10n_br_fiscal/i18n/l10n_br_fiscal.pot +24 -24
- odoo/addons/l10n_br_fiscal/migrations/18.0.3.0.0/pre-migration.py +30 -0
- odoo/addons/l10n_br_fiscal/models/document.py +1 -6
- odoo/addons/l10n_br_fiscal/models/document_line.py +35 -5
- odoo/addons/l10n_br_fiscal/models/document_line_mixin.py +63 -12
- odoo/addons/l10n_br_fiscal/models/document_line_mixin_methods.py +74 -52
- odoo/addons/l10n_br_fiscal/models/document_mixin.py +3 -3
- odoo/addons/l10n_br_fiscal/models/document_mixin_methods.py +28 -142
- odoo/addons/l10n_br_fiscal/models/document_related.py +1 -1
- odoo/addons/l10n_br_fiscal/security/fiscal_security.xml +6 -16
- odoo/addons/l10n_br_fiscal/static/description/index.html +1 -1
- odoo/addons/l10n_br_fiscal/tests/test_document_edition.py +113 -0
- odoo/addons/l10n_br_fiscal/tests/test_fiscal_document_generic.py +0 -11
- odoo/addons/l10n_br_fiscal/tests/test_fiscal_document_nfse.py +0 -1
- odoo/addons/l10n_br_fiscal/tests/test_tax_benefit.py +0 -1
- odoo/addons/l10n_br_fiscal/views/document_line_view.xml +3 -3
- odoo/addons/l10n_br_fiscal/views/document_view.xml +6 -6
- {odoo_addon_l10n_br_fiscal-18.0.2.0.0.11.dist-info → odoo_addon_l10n_br_fiscal-18.0.3.1.0.dist-info}/METADATA +2 -2
- {odoo_addon_l10n_br_fiscal-18.0.2.0.0.11.dist-info → odoo_addon_l10n_br_fiscal-18.0.3.1.0.dist-info}/RECORD +26 -25
- {odoo_addon_l10n_br_fiscal-18.0.2.0.0.11.dist-info → odoo_addon_l10n_br_fiscal-18.0.3.1.0.dist-info}/WHEEL +0 -0
- {odoo_addon_l10n_br_fiscal-18.0.2.0.0.11.dist-info → odoo_addon_l10n_br_fiscal-18.0.3.1.0.dist-info}/top_level.txt +0 -0
|
@@ -7,8 +7,12 @@ from lxml import etree
|
|
|
7
7
|
|
|
8
8
|
from odoo import Command, api, models
|
|
9
9
|
|
|
10
|
-
from ..constants.fiscal import CFOP_DESTINATION_EXPORT, FISCAL_IN
|
|
11
|
-
from ..constants.icms import
|
|
10
|
+
from ..constants.fiscal import CFOP_DESTINATION_EXPORT, FISCAL_IN, TAX_DOMAIN_ICMS
|
|
11
|
+
from ..constants.icms import (
|
|
12
|
+
ICMS_BASE_TYPE_DEFAULT,
|
|
13
|
+
ICMS_ORIGIN_DEFAULT,
|
|
14
|
+
ICMS_ST_BASE_TYPE_DEFAULT,
|
|
15
|
+
)
|
|
12
16
|
|
|
13
17
|
FISCAL_TAX_ID_FIELDS = [
|
|
14
18
|
"cofins_tax_id",
|
|
@@ -171,11 +175,11 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
171
175
|
# Total value of products or services
|
|
172
176
|
record.price_gross = round_curr.round(record.price_unit * record.quantity)
|
|
173
177
|
record.amount_fiscal = record.price_gross - record.discount_value
|
|
174
|
-
record.
|
|
178
|
+
record.fiscal_amount_tax = record.amount_tax_not_included
|
|
175
179
|
|
|
176
180
|
add_to_amount = sum(record[a] for a in record._add_fields_to_amount())
|
|
177
181
|
rm_to_amount = sum(record[r] for r in record._rm_fields_to_amount())
|
|
178
|
-
record.
|
|
182
|
+
record.fiscal_amount_untaxed = (
|
|
179
183
|
record.price_gross
|
|
180
184
|
- record.discount_value
|
|
181
185
|
+ add_to_amount
|
|
@@ -183,13 +187,17 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
183
187
|
)
|
|
184
188
|
|
|
185
189
|
# Valor do documento (NF)
|
|
186
|
-
record.
|
|
190
|
+
record.fiscal_amount_total = (
|
|
191
|
+
record.fiscal_amount_untaxed + record.fiscal_amount_tax
|
|
192
|
+
)
|
|
187
193
|
|
|
188
194
|
# Valor Liquido (TOTAL + IMPOSTOS - RETENÇÕES)
|
|
189
|
-
record.amount_taxed =
|
|
195
|
+
record.amount_taxed = (
|
|
196
|
+
record.fiscal_amount_total - record.amount_tax_withholding
|
|
197
|
+
)
|
|
190
198
|
|
|
191
199
|
# Valor do documento (NF) - RETENÇÕES
|
|
192
|
-
record.
|
|
200
|
+
record.fiscal_amount_total = record.amount_taxed
|
|
193
201
|
|
|
194
202
|
# Valor financeiro
|
|
195
203
|
if (
|
|
@@ -305,11 +313,16 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
305
313
|
for tax in mapping_result["taxes"].values():
|
|
306
314
|
taxes |= tax
|
|
307
315
|
line.fiscal_tax_ids = taxes
|
|
308
|
-
line.comment_ids = line.fiscal_operation_line_id.comment_ids
|
|
309
|
-
|
|
310
316
|
else:
|
|
311
317
|
line.fiscal_tax_ids = [Command.clear()]
|
|
312
318
|
|
|
319
|
+
@api.depends("fiscal_operation_line_id")
|
|
320
|
+
def _compute_comment_ids(self):
|
|
321
|
+
for line in self:
|
|
322
|
+
line.comment_ids = [
|
|
323
|
+
Command.set(line.fiscal_operation_line_id.comment_ids.ids)
|
|
324
|
+
]
|
|
325
|
+
|
|
313
326
|
@api.model
|
|
314
327
|
def _build_null_mask_dict(self) -> dict:
|
|
315
328
|
"""
|
|
@@ -465,48 +478,50 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
465
478
|
self.ensure_one()
|
|
466
479
|
return self.partner_id
|
|
467
480
|
|
|
468
|
-
@api.
|
|
469
|
-
def
|
|
470
|
-
if
|
|
481
|
+
@api.depends("product_id")
|
|
482
|
+
def _compute_product_fiscal_fields(self):
|
|
483
|
+
if self._context.get("skip_compute_product_fiscal_fields"):
|
|
471
484
|
return
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
485
|
+
for line in self:
|
|
486
|
+
if not line.product_id:
|
|
487
|
+
# reset to default values:
|
|
488
|
+
line.fiscal_type = False
|
|
489
|
+
line.ncm_id = False
|
|
490
|
+
line.nbm_id = False
|
|
491
|
+
line.tax_icms_or_issqn = TAX_DOMAIN_ICMS
|
|
492
|
+
line.icms_origin = ICMS_ORIGIN_DEFAULT
|
|
493
|
+
line.cest_id = False
|
|
494
|
+
line.nbs_id = False
|
|
495
|
+
line.fiscal_genre_id = False
|
|
496
|
+
line.service_type_id = False
|
|
497
|
+
line.city_taxation_code_id = False
|
|
498
|
+
line.issqn_fg_city_id = False
|
|
499
|
+
continue
|
|
500
|
+
|
|
501
|
+
product = line.product_id
|
|
502
|
+
line.fiscal_type = product.fiscal_type
|
|
503
|
+
line.ncm_id = product.ncm_id
|
|
504
|
+
line.nbm_id = product.nbm_id
|
|
505
|
+
line.tax_icms_or_issqn = product.tax_icms_or_issqn
|
|
506
|
+
line.icms_origin = product.icms_origin
|
|
507
|
+
line.cest_id = product.cest_id
|
|
508
|
+
line.nbs_id = product.nbs_id
|
|
509
|
+
line.fiscal_genre_id = product.fiscal_genre_id
|
|
510
|
+
line.service_type_id = product.service_type_id
|
|
511
|
+
if product.city_taxation_code_ids and line.company_id:
|
|
512
|
+
city = product.city_taxation_code_ids.filtered(
|
|
513
|
+
lambda r, current_line=line: r.city_id
|
|
514
|
+
== current_line.company_id.city_id
|
|
489
515
|
)
|
|
490
|
-
if
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
self.tax_icms_or_issqn = False
|
|
500
|
-
self.icms_origin = False
|
|
501
|
-
self.cest_id = False
|
|
502
|
-
self.nbs_id = False
|
|
503
|
-
self.fiscal_genre_id = False
|
|
504
|
-
self.service_type_id = False
|
|
505
|
-
self.city_taxation_code_id = False
|
|
506
|
-
self.uot_id = False
|
|
507
|
-
|
|
508
|
-
self._compute_price_unit_fiscal()
|
|
509
|
-
self._onchange_fiscal_operation_id()
|
|
516
|
+
if city:
|
|
517
|
+
line.city_taxation_code_id = city
|
|
518
|
+
line.issqn_fg_city_id = line.company_id.city_id
|
|
519
|
+
else:
|
|
520
|
+
line.city_taxation_code_id = False
|
|
521
|
+
line.issqn_fg_city_id = False
|
|
522
|
+
else:
|
|
523
|
+
line.city_taxation_code_id = False
|
|
524
|
+
line.issqn_fg_city_id = False
|
|
510
525
|
|
|
511
526
|
def _prepare_fields_issqn(self, tax_dict):
|
|
512
527
|
self.ensure_one()
|
|
@@ -762,11 +777,18 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
762
777
|
)
|
|
763
778
|
line.fiscal_tax_ids = fiscal_taxes + taxes
|
|
764
779
|
|
|
765
|
-
@api.depends("uom_id")
|
|
780
|
+
@api.depends("product_id", "uom_id")
|
|
766
781
|
def _compute_uot_id(self):
|
|
767
782
|
for line in self:
|
|
768
|
-
if
|
|
769
|
-
line.uot_id = line.uom_id
|
|
783
|
+
if line.uom_id:
|
|
784
|
+
line.uot_id = line.uom_id.id
|
|
785
|
+
elif line.product_id:
|
|
786
|
+
if line.product_id.uot_id:
|
|
787
|
+
line.uot_id = line.product_id.uot_id.id
|
|
788
|
+
else:
|
|
789
|
+
line.uot_id = line.product_id.uom_id.id
|
|
790
|
+
else:
|
|
791
|
+
line.uot_id = False
|
|
770
792
|
|
|
771
793
|
@api.onchange("price_unit")
|
|
772
794
|
def _onchange_price_unit_fiscal(self):
|
|
@@ -126,7 +126,7 @@ class FiscalDocumentMixin(models.AbstractModel):
|
|
|
126
126
|
help="Amount without discount.",
|
|
127
127
|
)
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
fiscal_amount_untaxed = fields.Monetary(
|
|
130
130
|
compute="_compute_fiscal_amount",
|
|
131
131
|
store=True,
|
|
132
132
|
)
|
|
@@ -394,12 +394,12 @@ class FiscalDocumentMixin(models.AbstractModel):
|
|
|
394
394
|
store=True,
|
|
395
395
|
)
|
|
396
396
|
|
|
397
|
-
|
|
397
|
+
fiscal_amount_tax = fields.Monetary(
|
|
398
398
|
compute="_compute_fiscal_amount",
|
|
399
399
|
store=True,
|
|
400
400
|
)
|
|
401
401
|
|
|
402
|
-
|
|
402
|
+
fiscal_amount_total = fields.Monetary(
|
|
403
403
|
compute="_compute_fiscal_amount",
|
|
404
404
|
store=True,
|
|
405
405
|
)
|
|
@@ -73,7 +73,8 @@ class FiscalDocumentMixinMethods(models.AbstractModel):
|
|
|
73
73
|
def _get_amount_fields(self):
|
|
74
74
|
"""Get all fields with 'amount_' prefix"""
|
|
75
75
|
fields = self.env["l10n_br_fiscal.document.mixin"]._fields.keys()
|
|
76
|
-
|
|
76
|
+
prefixes = ("amount_", "fiscal_amount_")
|
|
77
|
+
amount_fields = [f for f in fields if f.startswith(prefixes)]
|
|
77
78
|
return amount_fields
|
|
78
79
|
|
|
79
80
|
@api.depends("document_serie_id", "issuer")
|
|
@@ -198,152 +199,37 @@ class FiscalDocumentMixinMethods(models.AbstractModel):
|
|
|
198
199
|
elif doc.comment_ids is None:
|
|
199
200
|
doc.comment_ids = []
|
|
200
201
|
|
|
201
|
-
def
|
|
202
|
-
for record in self
|
|
203
|
-
if (
|
|
202
|
+
def _distribute_amount_to_lines(self, amount_field_name, line_field_name):
|
|
203
|
+
for record in self:
|
|
204
|
+
if not (
|
|
204
205
|
record.delivery_costs == "total"
|
|
205
206
|
or record.force_compute_delivery_costs_by_total
|
|
206
207
|
):
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
record.
|
|
217
|
-
|
|
218
|
-
- sum(
|
|
219
|
-
line.freight_value
|
|
220
|
-
for line in record._get_product_amount_lines()[:-1]
|
|
221
|
-
)
|
|
222
|
-
)
|
|
223
|
-
else:
|
|
224
|
-
amount_total = sum(
|
|
225
|
-
record._get_product_amount_lines().mapped("price_gross")
|
|
208
|
+
continue
|
|
209
|
+
lines = record._get_product_amount_lines()
|
|
210
|
+
if not lines:
|
|
211
|
+
continue
|
|
212
|
+
amount_to_distribute = record[amount_field_name]
|
|
213
|
+
total_gross = sum(lines.mapped("price_gross"))
|
|
214
|
+
if total_gross > 0:
|
|
215
|
+
distributed_amount = 0
|
|
216
|
+
for line in lines[:-1]:
|
|
217
|
+
proportional_amount = record.currency_id.round(
|
|
218
|
+
amount_to_distribute * (line.price_gross / total_gross)
|
|
226
219
|
)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
)
|
|
238
|
-
)
|
|
239
|
-
for line in record._get_product_amount_lines():
|
|
240
|
-
line._onchange_fiscal_taxes()
|
|
241
|
-
record._fields["amount_total"].compute_value(record)
|
|
242
|
-
record.write(
|
|
243
|
-
{
|
|
244
|
-
name: value
|
|
245
|
-
for name, value in record._cache.items()
|
|
246
|
-
if record._fields[name].compute == "_amount_all"
|
|
247
|
-
and not record._fields[name].inverse
|
|
248
|
-
}
|
|
249
|
-
)
|
|
220
|
+
line[line_field_name] = proportional_amount
|
|
221
|
+
distributed_amount += proportional_amount
|
|
222
|
+
lines[-1][line_field_name] = amount_to_distribute - distributed_amount
|
|
223
|
+
else:
|
|
224
|
+
lines.write({line_field_name: 0.0})
|
|
225
|
+
if lines:
|
|
226
|
+
lines[0][line_field_name] = amount_to_distribute
|
|
227
|
+
|
|
228
|
+
def _inverse_amount_freight(self):
|
|
229
|
+
self._distribute_amount_to_lines("amount_freight_value", "freight_value")
|
|
250
230
|
|
|
251
231
|
def _inverse_amount_insurance(self):
|
|
252
|
-
|
|
253
|
-
if (
|
|
254
|
-
record.delivery_costs == "total"
|
|
255
|
-
or record.force_compute_delivery_costs_by_total
|
|
256
|
-
):
|
|
257
|
-
amount_insurance_value = record.amount_insurance_value
|
|
258
|
-
if all(record._get_product_amount_lines().mapped("insurance_value")):
|
|
259
|
-
amount_insurance_old = sum(
|
|
260
|
-
record._get_product_amount_lines().mapped("insurance_value")
|
|
261
|
-
)
|
|
262
|
-
for line in record._get_product_amount_lines()[:-1]:
|
|
263
|
-
line.insurance_value = amount_insurance_value * (
|
|
264
|
-
line.insurance_value / amount_insurance_old
|
|
265
|
-
)
|
|
266
|
-
record._get_product_amount_lines()[-1].insurance_value = (
|
|
267
|
-
amount_insurance_value
|
|
268
|
-
- sum(
|
|
269
|
-
line.insurance_value
|
|
270
|
-
for line in record._get_product_amount_lines()[:-1]
|
|
271
|
-
)
|
|
272
|
-
)
|
|
273
|
-
else:
|
|
274
|
-
amount_total = sum(
|
|
275
|
-
record._get_product_amount_lines().mapped("price_gross")
|
|
276
|
-
)
|
|
277
|
-
for line in record._get_product_amount_lines()[:-1]:
|
|
278
|
-
if line.price_gross and amount_total:
|
|
279
|
-
line.insurance_value = amount_insurance_value * (
|
|
280
|
-
line.price_gross / amount_total
|
|
281
|
-
)
|
|
282
|
-
record._get_product_amount_lines()[-1].insurance_value = (
|
|
283
|
-
amount_insurance_value
|
|
284
|
-
- sum(
|
|
285
|
-
line.insurance_value
|
|
286
|
-
for line in record._get_product_amount_lines()[:-1]
|
|
287
|
-
)
|
|
288
|
-
)
|
|
289
|
-
for line in record._get_product_amount_lines():
|
|
290
|
-
line._onchange_fiscal_taxes()
|
|
291
|
-
record._fields["amount_total"].compute_value(record)
|
|
292
|
-
record.write(
|
|
293
|
-
{
|
|
294
|
-
name: value
|
|
295
|
-
for name, value in record._cache.items()
|
|
296
|
-
if record._fields[name].compute == "_amount_all"
|
|
297
|
-
and not record._fields[name].inverse
|
|
298
|
-
}
|
|
299
|
-
)
|
|
232
|
+
self._distribute_amount_to_lines("amount_insurance_value", "insurance_value")
|
|
300
233
|
|
|
301
234
|
def _inverse_amount_other(self):
|
|
302
|
-
|
|
303
|
-
if (
|
|
304
|
-
record.delivery_costs == "total"
|
|
305
|
-
or record.force_compute_delivery_costs_by_total
|
|
306
|
-
):
|
|
307
|
-
amount_other_value = record.amount_other_value
|
|
308
|
-
if all(record._get_product_amount_lines().mapped("other_value")):
|
|
309
|
-
amount_other_old = sum(
|
|
310
|
-
record._get_product_amount_lines().mapped("other_value")
|
|
311
|
-
)
|
|
312
|
-
for line in record._get_product_amount_lines()[:-1]:
|
|
313
|
-
line.other_value = amount_other_value * (
|
|
314
|
-
line.other_value / amount_other_old
|
|
315
|
-
)
|
|
316
|
-
record._get_product_amount_lines()[-1].other_value = (
|
|
317
|
-
amount_other_value
|
|
318
|
-
- sum(
|
|
319
|
-
line.other_value
|
|
320
|
-
for line in record._get_product_amount_lines()[:-1]
|
|
321
|
-
)
|
|
322
|
-
)
|
|
323
|
-
else:
|
|
324
|
-
amount_total = sum(
|
|
325
|
-
record._get_product_amount_lines().mapped("price_gross")
|
|
326
|
-
)
|
|
327
|
-
for line in record._get_product_amount_lines()[:-1]:
|
|
328
|
-
if line.price_gross and amount_total:
|
|
329
|
-
line.other_value = amount_other_value * (
|
|
330
|
-
line.price_gross / amount_total
|
|
331
|
-
)
|
|
332
|
-
record._get_product_amount_lines()[-1].other_value = (
|
|
333
|
-
amount_other_value
|
|
334
|
-
- sum(
|
|
335
|
-
line.other_value
|
|
336
|
-
for line in record._get_product_amount_lines()[:-1]
|
|
337
|
-
)
|
|
338
|
-
)
|
|
339
|
-
for line in record._get_product_amount_lines():
|
|
340
|
-
line._onchange_fiscal_taxes()
|
|
341
|
-
record._fields["amount_total"].compute_value(record)
|
|
342
|
-
record.write(
|
|
343
|
-
{
|
|
344
|
-
name: value
|
|
345
|
-
for name, value in record._cache.items()
|
|
346
|
-
if record._fields[name].compute == "_amount_all"
|
|
347
|
-
and not record._fields[name].inverse
|
|
348
|
-
}
|
|
349
|
-
)
|
|
235
|
+
self._distribute_amount_to_lines("amount_other_value", "other_value")
|
|
@@ -104,7 +104,7 @@ class DocumentRelated(models.Model):
|
|
|
104
104
|
return False
|
|
105
105
|
|
|
106
106
|
self.document_type_id = related.document_type_id
|
|
107
|
-
self.document_total_amount = related.
|
|
107
|
+
self.document_total_amount = related.fiscal_amount_total
|
|
108
108
|
self.document_total_weight = related.total_weight
|
|
109
109
|
|
|
110
110
|
if related.document_type_id.electronic:
|
|
@@ -38,44 +38,34 @@
|
|
|
38
38
|
<field name="name">Fiscal Tax Estimate multi-company</field>
|
|
39
39
|
<field name="model_id" ref="model_l10n_br_fiscal_tax_estimate" />
|
|
40
40
|
<field eval="True" name="global" />
|
|
41
|
-
<field
|
|
42
|
-
name="domain_force"
|
|
43
|
-
>['|',('company_id','=',False),('company_id','in',company_ids)]</field>
|
|
41
|
+
<field name="domain_force">[('company_id', 'in', company_ids + [False])]</field>
|
|
44
42
|
</record>
|
|
45
43
|
|
|
46
|
-
<record id="
|
|
44
|
+
<record id="l10n_br_fiscal_operation_rule" model="ir.rule">
|
|
47
45
|
<field name="name">Fiscal Operation multi-company</field>
|
|
48
46
|
<field name="model_id" ref="model_l10n_br_fiscal_operation" />
|
|
49
47
|
<field eval="True" name="global" />
|
|
50
|
-
<field
|
|
51
|
-
name="domain_force"
|
|
52
|
-
>['|',('company_id','=',False),('company_id','in',company_ids)]</field>
|
|
48
|
+
<field name="domain_force">[('company_id', 'in', company_ids + [False])]</field>
|
|
53
49
|
</record>
|
|
54
50
|
|
|
55
51
|
<record id="l10n_br_fiscal_document_serie_rule" model="ir.rule">
|
|
56
52
|
<field name="name">Fiscal Document Serie multi-company</field>
|
|
57
53
|
<field name="model_id" ref="model_l10n_br_fiscal_document_serie" />
|
|
58
54
|
<field eval="True" name="global" />
|
|
59
|
-
<field
|
|
60
|
-
name="domain_force"
|
|
61
|
-
>['|',('company_id','=',False),('company_id','in',company_ids)]</field>
|
|
55
|
+
<field name="domain_force">[('company_id', 'in', company_ids + [False])]</field>
|
|
62
56
|
</record>
|
|
63
57
|
|
|
64
58
|
<record id="l10n_br_fiscal_document_rule" model="ir.rule">
|
|
65
59
|
<field name="name">Fiscal Document multi-company</field>
|
|
66
60
|
<field name="model_id" ref="model_l10n_br_fiscal_document" />
|
|
67
61
|
<field eval="True" name="global" />
|
|
68
|
-
<field
|
|
69
|
-
name="domain_force"
|
|
70
|
-
>['|',('company_id','=',False),('company_id','in',company_ids)]</field>
|
|
62
|
+
<field name="domain_force">[('company_id', 'in', company_ids + [False])]</field>
|
|
71
63
|
</record>
|
|
72
64
|
|
|
73
65
|
<record id="l10n_br_fiscal_document_line_rule" model="ir.rule">
|
|
74
66
|
<field name="name">Fiscal Document line multi-company</field>
|
|
75
67
|
<field name="model_id" ref="model_l10n_br_fiscal_document_line" />
|
|
76
68
|
<field eval="True" name="global" />
|
|
77
|
-
<field
|
|
78
|
-
name="domain_force"
|
|
79
|
-
>['|',('company_id','=',False),('company_id','in',company_ids)]</field>
|
|
69
|
+
<field name="domain_force">[('company_id', 'in', company_ids + [False])]</field>
|
|
80
70
|
</record>
|
|
81
71
|
</odoo>
|
|
@@ -372,7 +372,7 @@ ul.auto-toc {
|
|
|
372
372
|
!! This file is generated by oca-gen-addon-readme !!
|
|
373
373
|
!! changes will be overwritten. !!
|
|
374
374
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
375
|
-
!! source digest: sha256:
|
|
375
|
+
!! source digest: sha256:306bfd80e3ccb9e7d199cfffc21bda642881eece97f1763ad8f6009904d198f4
|
|
376
376
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
|
377
377
|
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/l10n-brazil/tree/18.0/l10n_br_fiscal"><img alt="OCA/l10n-brazil" src="https://img.shields.io/badge/github-OCA%2Fl10n--brazil-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/l10n-brazil-18-0/l10n-brazil-18-0-l10n_br_fiscal"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/l10n-brazil&target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
|
378
378
|
<p><img alt="image" src="https://raw.githubusercontent.com/OCA/l10n-brazil/18.0/l10n_br_fiscal/static/img/fiscal_dashboard.png" /></p>
|
|
@@ -35,6 +35,10 @@ class TestDocumentEdition(TransactionCase):
|
|
|
35
35
|
cls.env = cls.env(
|
|
36
36
|
user=cls.user, context=dict(cls.env.context, tracking_disable=True)
|
|
37
37
|
)
|
|
38
|
+
cls.user = cls.env.user
|
|
39
|
+
cls.company = cls.env.ref("l10n_br_base.empresa_lucro_presumido")
|
|
40
|
+
cls.user.company_ids |= cls.company
|
|
41
|
+
cls.user.company_id = cls.company.id
|
|
38
42
|
|
|
39
43
|
def test_basic_doc_edition(self):
|
|
40
44
|
doc_form = Form(
|
|
@@ -162,3 +166,112 @@ class TestDocumentEdition(TransactionCase):
|
|
|
162
166
|
self.assertEqual(doc.fiscal_line_ids[0].fiscal_price, 112)
|
|
163
167
|
self.assertEqual(doc.fiscal_line_ids[0].quantity, 10)
|
|
164
168
|
self.assertEqual(doc.fiscal_line_ids[0].fiscal_quantity, 5)
|
|
169
|
+
|
|
170
|
+
def test_landed_costs_by_line_and_by_total(self):
|
|
171
|
+
"""
|
|
172
|
+
Tests both landed cost scenarios: 'by line' and 'by total'.
|
|
173
|
+
1. By Line: Enters costs on lines and verifies the header totals.
|
|
174
|
+
2. By Total: Enters costs on the header and verifies lines distribution.
|
|
175
|
+
"""
|
|
176
|
+
self.env.user.groups_id |= self.env.ref("l10n_br_fiscal.group_user")
|
|
177
|
+
product1 = self.env.ref("product.product_product_6")
|
|
178
|
+
product2 = self.env.ref("product.product_product_7")
|
|
179
|
+
|
|
180
|
+
# Part 1: Test with delivery_costs = 'line'
|
|
181
|
+
# ----------------------------------------------------
|
|
182
|
+
self.company.delivery_costs = "line"
|
|
183
|
+
doc_form = Form(self.env["l10n_br_fiscal.document"])
|
|
184
|
+
doc_form.company_id = self.company
|
|
185
|
+
doc_form.partner_id = self.env.ref("l10n_br_base.res_partner_cliente1_sp")
|
|
186
|
+
doc_form.fiscal_operation_id = self.env.ref("l10n_br_fiscal.fo_venda")
|
|
187
|
+
|
|
188
|
+
with doc_form.fiscal_line_ids.new() as line1:
|
|
189
|
+
line1.product_id = product1
|
|
190
|
+
line1.fiscal_operation_line_id = self.env.ref(
|
|
191
|
+
"l10n_br_fiscal.fo_venda_venda"
|
|
192
|
+
)
|
|
193
|
+
line1.price_unit = 1000.0
|
|
194
|
+
line1.quantity = 2.0 # Gross: 2000
|
|
195
|
+
line1.freight_value = 10.0
|
|
196
|
+
line1.insurance_value = 20.0
|
|
197
|
+
line1.other_value = 5.0
|
|
198
|
+
|
|
199
|
+
with doc_form.fiscal_line_ids.new() as line2:
|
|
200
|
+
line2.product_id = product2
|
|
201
|
+
line2.fiscal_operation_line_id = self.env.ref(
|
|
202
|
+
"l10n_br_fiscal.fo_venda_venda"
|
|
203
|
+
)
|
|
204
|
+
line2.price_unit = 500.0
|
|
205
|
+
line2.quantity = 1.0 # Gross: 500
|
|
206
|
+
line2.freight_value = 4.0
|
|
207
|
+
line2.insurance_value = 6.0
|
|
208
|
+
line2.other_value = 2.0
|
|
209
|
+
|
|
210
|
+
doc = doc_form.save()
|
|
211
|
+
|
|
212
|
+
self.assertEqual(doc.company_id.delivery_costs, "line")
|
|
213
|
+
# Assert header totals are the SUM of line values
|
|
214
|
+
self.assertAlmostEqual(doc.amount_freight_value, 14.0) # 10.0 + 4.0
|
|
215
|
+
self.assertAlmostEqual(doc.amount_insurance_value, 26.0) # 20.0 + 6.0
|
|
216
|
+
self.assertAlmostEqual(doc.amount_other_value, 7.0) # 5.0 + 2.0
|
|
217
|
+
|
|
218
|
+
# Assert final fiscal totals (bottom-up calculation)
|
|
219
|
+
# price_gross = (1000*2) + (500*1) = 2500
|
|
220
|
+
# landed_costs = 14 + 26 + 7 = 47
|
|
221
|
+
# fiscal_amount_untaxed (IPI Base) = 2500 + 47 = 2547
|
|
222
|
+
self.assertAlmostEqual(doc.fiscal_amount_untaxed, 2547.00)
|
|
223
|
+
# fiscal_amount_tax (IPI) = (2035 * 3.25%) + (512 * 5%) = 66.14 + 25.60 = 91.74
|
|
224
|
+
self.assertAlmostEqual(doc.fiscal_amount_tax, 91.74, places=2)
|
|
225
|
+
# fiscal_amount_total = 2547.00 + 91.74 = 2638.74
|
|
226
|
+
self.assertAlmostEqual(doc.fiscal_amount_total, 2638.74, places=2)
|
|
227
|
+
|
|
228
|
+
# Part 2: Test with delivery_costs = 'total'
|
|
229
|
+
# ----------------------------------------------------
|
|
230
|
+
self.company.delivery_costs = "total"
|
|
231
|
+
doc_form_edit = Form(doc)
|
|
232
|
+
# Set new header totals, which should trigger inverse methods to distribute
|
|
233
|
+
doc_form_edit.amount_freight_value = 30.0
|
|
234
|
+
doc_form_edit.amount_insurance_value = 60.0
|
|
235
|
+
doc_form_edit.amount_other_value = 90.0
|
|
236
|
+
doc_after_total_update = doc_form_edit.save()
|
|
237
|
+
|
|
238
|
+
line1 = doc_after_total_update.fiscal_line_ids[0]
|
|
239
|
+
line2 = doc_after_total_update.fiscal_line_ids[1]
|
|
240
|
+
|
|
241
|
+
# Assert values were distributed proportionally to price_gross
|
|
242
|
+
# (2000 vs 500 -> 80% vs 20%)
|
|
243
|
+
# Freight: 30.0 * 0.8 = 24.0 | 30.0 * 0.2 = 6.0
|
|
244
|
+
self.assertAlmostEqual(line1.freight_value, 24.0)
|
|
245
|
+
self.assertAlmostEqual(line2.freight_value, 6.0)
|
|
246
|
+
# Insurance: 60.0 * 0.8 = 48.0 | 60.0 * 0.2 = 12.0
|
|
247
|
+
self.assertAlmostEqual(line1.insurance_value, 48.0)
|
|
248
|
+
self.assertAlmostEqual(line2.insurance_value, 12.0)
|
|
249
|
+
# Other: 90.0 * 0.8 = 72.0 | 90.0 * 0.2 = 18.0
|
|
250
|
+
self.assertAlmostEqual(line1.other_value, 72.0)
|
|
251
|
+
self.assertAlmostEqual(line2.other_value, 18.0)
|
|
252
|
+
|
|
253
|
+
# Assert final fiscal totals are recomputed correctly (top-down calculation)
|
|
254
|
+
# price_gross = 2500
|
|
255
|
+
# landed_costs = 30 + 60 + 90 = 180
|
|
256
|
+
# fiscal_amount_untaxed (IPI Base) = 2500 + 180 = 2680
|
|
257
|
+
self.assertAlmostEqual(doc_after_total_update.fiscal_amount_untaxed, 2680.00)
|
|
258
|
+
# Line 1 IPI Base = 2000 (product) + 24 (freight) + 48 (insurance)
|
|
259
|
+
# + 72 (other) = 2144
|
|
260
|
+
# Line 1 IPI Value = 2144 * 3.25% = 69.68
|
|
261
|
+
self.assertAlmostEqual(line1.ipi_base, 2144.00)
|
|
262
|
+
self.assertAlmostEqual(line1.ipi_value, 69.68, places=2)
|
|
263
|
+
|
|
264
|
+
# Line 2 IPI Base = 500 (product) + 6 (freight) + 12 (insurance)
|
|
265
|
+
# + 18 (other) = 536
|
|
266
|
+
# Line 2 IPI Value = 536 * 5% = 26.80
|
|
267
|
+
self.assertAlmostEqual(line2.ipi_base, 536.00)
|
|
268
|
+
self.assertAlmostEqual(line2.ipi_value, 26.80, places=2)
|
|
269
|
+
|
|
270
|
+
# fiscal_amount_tax (IPI) = 69.68 + 26.80 = 96.48
|
|
271
|
+
self.assertAlmostEqual(
|
|
272
|
+
doc_after_total_update.fiscal_amount_tax, 96.48, places=2
|
|
273
|
+
)
|
|
274
|
+
# fiscal_amount_total = 2680.00 + 96.48 = 2776.48
|
|
275
|
+
self.assertAlmostEqual(
|
|
276
|
+
doc_after_total_update.fiscal_amount_total, 2776.48, places=2
|
|
277
|
+
)
|