Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[14.0][IMP] l10n_br_purchase_stock: Tornando a Fatura criada pela Ordem de Seleção/stock.picking semelhante a do Pedido de Compra/purchase.order, evitando "glue modules" #3299

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions l10n_br_purchase_stock/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"license": "AGPL-3",
"category": "Localisation",
"author": "Akretion, Odoo Community Association (OCA)",
"maintainers": ["renatonlima", "mbcosta"],
"website": "https://github.com/OCA/l10n-brazil",
"version": "14.0.2.0.1",
"depends": [
Expand Down
80 changes: 80 additions & 0 deletions l10n_br_purchase_stock/demo/purchase_order.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,46 @@
<value eval="[ref('main_pl_only_products_1_2')]" />
</function>

<!-- Section -->
<record id="main_pl_only_products_1_3" model="purchase.order.line">
<field name="order_id" ref="main_po_only_products_1" />
<field name="name">TEST SECTION 1</field>
<field name="display_type">line_section</field>
<field name="product_qty">0</field>
</record>

<!-- Note -->
<record id="main_pl_only_products_1_4" model="purchase.order.line">
<field name="order_id" ref="main_po_only_products_1" />
<field name="name">TEST NOTE 1</field>
<field name="display_type">line_note</field>
<field name="product_qty">0</field>
</record>

<record id="main_pl_only_products_1_5" model="purchase.order.line">
<field name="order_id" ref="main_po_only_products_1" />
<field name="product_id" ref="product.product_product_8" />
<field name="product_qty">2</field>
<field name="product_uom" ref="uom.product_uom_unit" />
<field name="price_unit">500</field>
<field name="fiscal_operation_id" ref="l10n_br_fiscal.fo_compras" />
<field
name="fiscal_operation_line_id"
ref="l10n_br_fiscal.fo_compras_compras"
/>
<field name="date_planned" eval="time.strftime('%Y-%m-%d')" />
<field name="partner_order">999999</field>
<field name="partner_order_line">003</field>
<field name="manual_additional_data">Teste - Additional Data</field>
<field name="insurance_value">10</field>
<field name="other_value">10</field>
<field name="freight_value">10</field>
</record>

<function model="purchase.order.line" name="_onchange_product_id_fiscal">
<value eval="[ref('main_pl_only_products_1_5')]" />
</function>

<!-- Purchase Order with only products test 2 - Teste Agrupamento -->
<record id="main_po_only_products_2" model="purchase.order">
<field name="name">Main l10n_br_purchase_stock - teste agrupamento</field>
Expand Down Expand Up @@ -170,6 +210,46 @@
<value eval="[ref('main_pl_only_products_2_2')]" />
</function>

<!-- Section -->
<record id="main_pl_only_products_2_3" model="purchase.order.line">
<field name="order_id" ref="main_po_only_products_2" />
<field name="name">TEST SECTION 2</field>
<field name="display_type">line_section</field>
<field name="product_qty">0</field>
</record>

<!-- Note -->
<record id="main_pl_only_products_2_4" model="purchase.order.line">
<field name="order_id" ref="main_po_only_products_2" />
<field name="name">TEST NOTE 2</field>
<field name="display_type">line_note</field>
<field name="product_qty">0</field>
</record>

<record id="main_pl_only_products_2_5" model="purchase.order.line">
<field name="order_id" ref="main_po_only_products_2" />
<field name="product_id" ref="product.product_product_8" />
<field name="product_qty">2</field>
<field name="product_uom" ref="uom.product_uom_unit" />
<field name="price_unit">500</field>
<field name="fiscal_operation_id" ref="l10n_br_fiscal.fo_compras" />
<field
name="fiscal_operation_line_id"
ref="l10n_br_fiscal.fo_compras_compras"
/>
<field name="date_planned" eval="time.strftime('%Y-%m-%d')" />
<field name="partner_order">999999</field>
<field name="partner_order_line">003</field>
<field name="manual_additional_data">Teste - Additional Data</field>
<field name="insurance_value">10</field>
<field name="other_value">10</field>
<field name="freight_value">10</field>
</record>

<function model="purchase.order.line" name="_onchange_product_id_fiscal">
<value eval="[ref('main_pl_only_products_2_5')]" />
</function>

<!-- Lucro Presumido -->
<!-- Purchase Order with only products test -->
<record id="lucro_presumido_po_only_products_1" model="purchase.order">
Expand Down
15 changes: 13 additions & 2 deletions l10n_br_purchase_stock/tests/test_l10n_br_purchase_stock.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def test_grouping_pickings(self):
self.assertIn(picking_2, invoice.picking_ids)

# Validar o price_unit usado
for inv_line in invoice.invoice_line_ids:
for inv_line in invoice.invoice_line_ids.filtered(lambda ln: ln.product_id):
# TODO: A forma de instalação dos modulos feita no CI
# falha o browse aqui
# l10n_br_stock_account/models/stock_invoice_onshipping.py:105
Expand All @@ -72,6 +72,17 @@ def test_grouping_pickings(self):
inv_line.fiscal_operation_line_id, "Missing Fiscal Operation Line."
)

# Section Lines
section_lines = invoice.invoice_line_ids.filtered(
lambda ln: ln.display_type == "line_section"
)
self.assertEqual(len(section_lines), 2)
# Note Lines
note_lines = invoice.invoice_line_ids.filtered(
lambda ln: ln.display_type == "line_note"
)
self.assertEqual(len(note_lines), 2)

if hasattr(invoice, "document_serie"):
invoice.document_serie = "1"
invoice.document_number = "123"
Expand Down Expand Up @@ -150,7 +161,7 @@ def test_purchase_order_lucro_presumido(self):
invoice = self.create_invoice_wizard(picking)

# Validar o price_unit usado
for inv_line in invoice.invoice_line_ids:
for inv_line in invoice.invoice_line_ids.filtered(lambda ln: ln.product_id):
# TODO: A forma de instalação dos modulos feita no CI
# falha o browse aqui
# l10n_br_stock_account/models/stock_invoice_onshipping.py:105
Expand Down
196 changes: 174 additions & 22 deletions l10n_br_purchase_stock/wizards/stock_invocing_onshipping.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Magno Costa <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo import fields, models
from odoo import models

from odoo.addons.l10n_br_fiscal.constants.fiscal import DOCUMENT_ISSUER_PARTNER

Expand All @@ -18,16 +18,76 @@ def _build_invoice_values_from_pickings(self, pickings):
"""
invoice, values = super()._build_invoice_values_from_pickings(pickings)

pick = fields.first(pickings)
if pick.purchase_id:
values["purchase_id"] = pick.purchase_id.id
values["issuer"] = DOCUMENT_ISSUER_PARTNER
purchase_pickings = pickings.filtered(lambda pk: pk.purchase_id)
if purchase_pickings and self._get_invoice_type() != "in_refund":
# Case more than one Purchase Order the fields below will be join
# the others will be overwritting, as done in purchase module,
# one more field include here Note
payment_refs = set()
refs = set()
# Include Note/Narration
narration = set()
for picking in purchase_pickings:
# Campos informados em qualquer caso
purchase = picking.purchase_id

if pick.purchase_id.payment_term_id.id != values.get(
"invoice_payment_term_id"
):
# Campo purchase_id store=false
# values["purchase_id"] = purchase.id
if picking.fiscal_operation_id:
values["issuer"] = DOCUMENT_ISSUER_PARTNER

# Refund case don't get values from Purchase Dict
# TODO: Should get any value?
purchase_values = purchase._prepare_invoice()

# Fields to Join
# origins.add(purchase_values["invoice_origin"])
payment_refs.add(purchase_values["payment_reference"])
refs.add(purchase_values["ref"])
narration.add(purchase_values["narration"])

# Original dict from purchase module.

# Fields to get from original dict:
# - "ref": self.partner_ref or "",
# - "narration": self.notes,
# - "currency_id": self.currency_id.id,
# - "invoice_user_id": self.user_id and self.user_id.id
# or self.env.user.id,
# - "payment_reference": self.partner_ref or "",
# - "partner_bank_id": partner_bank_id.id,
# - "invoice_payment_term_id": self.payment_term_id.id,

# Fields to remove from Original Dict
vals_to_remove = {
"move_type",
"partner_id",
"fiscal_position_id",
"invoice_origin",
"invoice_line_ids",
"company_id",
# Another fields
"__last_update",
"display_name",
}

purchase_values_rm = {
k: purchase_values[k] for k in set(purchase_values) - vals_to_remove
}
values.update(purchase_values_rm)

# Fields to join
if len(purchase_pickings) > 1:
values.update(
{"invoice_payment_term_id": pick.purchase_id.payment_term_id.id}
{
"ref": ", ".join(refs)[:2000],
# In this case Origin get Pickings Names
# "invoice_origin": ", ".join(origins),
"payment_reference": len(payment_refs) == 1
and payment_refs.pop()
or False,
"narration": ", ".join(narration),
}
)

return invoice, values
Expand All @@ -40,8 +100,7 @@ def _get_move_key(self, move):
"""
key = super()._get_move_key(move)
if move.purchase_line_id:
# TODO: deveria permitir agrupar as linhas ?
# Deveria permitir agrupar Pedidos de Compras ?
# Field purchase_line_id in account.move is Many2one
key = key + (move.purchase_line_id,)

return key
Expand All @@ -57,16 +116,109 @@ def _get_invoice_line_values(self, moves, invoice_values, invoice):
values = super()._get_invoice_line_values(moves, invoice_values, invoice)
# Devido ao KEY com purchase_line_id aqui
# vem somente um registro
if len(moves) == 1:
# Caso venha apenas uma linha porem sem
# purchase_line_id é preciso ignora-la
if moves.purchase_line_id:
values["purchase_line_id"] = moves.purchase_line_id.id
values[
"analytic_account_id"
] = moves.purchase_line_id.account_analytic_id.id
values["analytic_tag_ids"] = [
(6, 0, moves.purchase_line_id.analytic_tag_ids.ids)
]
purchase_moves = moves.filtered(lambda ln: ln.purchase_line_id)
if purchase_moves:
purchase_line = purchase_moves.purchase_line_id
# Campos informados em qualquer caso
values["purchase_line_id"] = purchase_line.id
values["analytic_account_id"] = purchase_line.account_analytic_id.id
values["analytic_tag_ids"] = [(6, 0, purchase_line.analytic_tag_ids.ids)]

# Refund case don't get values from Purchase Line Dict
# TODO: Should get any value?
if self._get_invoice_type() != "in_refund":
# Same make above, get fields informed in
# original of Purchase Line dict:
purchase_line_values = purchase_line._prepare_account_move_line()

# Fields to get:
# "display_type": self.display_type,
# "sequence": self.sequence,

# Fields to remove:
vals_to_remove = {
"name",
"product_id",
"product_uom_id",
"quantity",
"price_unit",
"tax_ids",
"analytic_account_id",
"analytic_tag_ids",
"purchase_line_id",
# another fields
"__last_update",
"display_name",
}

purchase_line_values_rm = {
k: purchase_line_values[k]
for k in set(purchase_line_values) - vals_to_remove
}
values.update(purchase_line_values_rm)

return values

def _create_invoice(self, invoice_values):
"""Override this method if you need to change any values of the
invoice and the lines before the invoice creation
:param invoice_values: dict with the invoice and its lines
:return: invoice
"""
purchase = self.env["purchase.order"].browse(invoice_values.get("purchase_id"))
pickings = self._load_pickings()
purchase_pickings = pickings.filtered(lambda pk: pk.purchase_id)
if not purchase_pickings or self._get_invoice_type() == "in_refund":
return super()._create_invoice(invoice_values)

# Check Other Purchase Lines
section_note_lines = self.env["purchase.order.line"]
# Resequencing
invoice_item_sequence = 10
invoice_item_seq_dict = {}
for picking in purchase_pickings.sorted(key=lambda p: p.name):
purchase = picking.purchase_id
# Resequencing
for line in purchase.order_line:
invoice_item_seq_dict[line.id] = invoice_item_sequence
invoice_item_sequence += 1

# Section and Note Lines
section_note_lines |= purchase.order_line.filtered(
lambda ln: ln.display_type in ("line_section", "line_note")
)

for line in section_note_lines:
line_vals = line._prepare_account_move_line()
invoice_values["invoice_line_ids"].append((0, 0, line_vals))

# Resequence
for ln in invoice_values["invoice_line_ids"]:
if ln[0] != 5:
if ln[2] and ln[2].get("purchase_line_id"):
ln[2].update(
{
"sequence": invoice_item_seq_dict.get(
ln[2].get("purchase_line_id")
)
}
)

# 3) Create invoices.
moves = self.env["account.move"]
AccountMove = self.env["account.move"].with_context(
default_move_type="in_invoice"
)
# for vals in invoice_vals_list:
moves |= AccountMove.with_company(self.env.company).create(invoice_values)

# 4) Some moves might actually be refunds: convert them if the
# total amount is negative
# We do this after the moves have been created since we need taxes,
# etc. to know if the total
# is actually negative or not
moves.filtered(
lambda m: m.currency_id.round(m.amount_total) < 0
).action_switch_invoice_into_refund_credit_note()

return moves
Loading