[IMP] l10n_in_edi: allow negative line for in invoice
Indian edi does not support line with a negative subtotal but this use case is common when you want to apply a global discount. This commit modify the json generated. The negative line are splitted on same HSN code of the positive line affecting the discount of the positive line. The total of the invoice and the taxes remain unchanged. closes odoo/odoo#109389 X-original-commit: 80701911229af6b9a6a29420796e45fcdbbd13ec Signed-off-by: Quentin De Paoli <qdp@odoo.com> Signed-off-by: Josse Colpaert <jco@odoo.com>
This commit is contained in:
@@ -6,9 +6,11 @@ import json
|
||||
import pytz
|
||||
import markupsafe
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.tools import html_escape, float_is_zero
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo.tools import html_escape, float_is_zero, float_compare
|
||||
from odoo.exceptions import AccessError, ValidationError
|
||||
from odoo.addons.iap import jsonrpc
|
||||
import logging
|
||||
|
||||
@@ -69,6 +71,11 @@ class AccountEdiFormat(models.Model):
|
||||
if not re.match("^.{1,16}$", move.name):
|
||||
error_message.append(_("Invoice number should not be more than 16 characters"))
|
||||
for line in move.invoice_line_ids.filtered(lambda line: line.display_type not in ('line_note', 'line_section', 'rounding')):
|
||||
if line.price_subtotal < 0:
|
||||
# Line having a negative amount is not allowed.
|
||||
if not move._l10n_in_edi_is_managing_invoice_negative_lines_allowed():
|
||||
raise ValidationError(_("Invoice lines having a negative amount are not allowed to generate the IRN. "
|
||||
"Please create a credit note instead."))
|
||||
if line.product_id:
|
||||
hsn_code = self._l10n_in_edi_extract_digits(line.product_id.l10n_in_hsn_code)
|
||||
if not hsn_code:
|
||||
@@ -299,7 +306,8 @@ class AccountEdiFormat(models.Model):
|
||||
"""
|
||||
sign = line.move_id.is_inbound() and -1 or 1
|
||||
tax_details_by_code = self._get_l10n_in_tax_details_by_line_code(line_tax_details.get("tax_details", {}))
|
||||
full_discount_or_zero_quantity = line.discount == 100.00 or float_is_zero(line.quantity, 3)
|
||||
quantity = line.quantity
|
||||
full_discount_or_zero_quantity = line.discount == 100.00 or float_is_zero(quantity, 3)
|
||||
if full_discount_or_zero_quantity:
|
||||
unit_price_in_inr = line.currency_id._convert(
|
||||
line.price_unit,
|
||||
@@ -308,19 +316,26 @@ class AccountEdiFormat(models.Model):
|
||||
line.date or fields.Date.context_today(self)
|
||||
)
|
||||
else:
|
||||
unit_price_in_inr = ((sign * line.balance) / (1 - (line.discount / 100))) / line.quantity
|
||||
unit_price_in_inr = ((sign * line.balance) / (1 - (line.discount / 100))) / quantity
|
||||
|
||||
if unit_price_in_inr < 0 and quantity < 0:
|
||||
# If unit price and quantity both is negative then
|
||||
# We set unit price and quantity as positive because
|
||||
# government does not accept negative in qty or unit price
|
||||
unit_price_in_inr = unit_price_in_inr * -1
|
||||
quantity = quantity * -1
|
||||
return {
|
||||
"SlNo": str(index),
|
||||
"PrdDesc": line.name.replace("\n", ""),
|
||||
"IsServc": line.product_id.type == "service" and "Y" or "N",
|
||||
"HsnCd": self._l10n_in_edi_extract_digits(line.product_id.l10n_in_hsn_code),
|
||||
"Qty": self._l10n_in_round_value(line.quantity or 0.0, 3),
|
||||
"Qty": self._l10n_in_round_value(quantity or 0.0, 3),
|
||||
"Unit": line.product_uom_id.l10n_in_code and line.product_uom_id.l10n_in_code.split("-")[0] or "OTH",
|
||||
# Unit price in company currency and tax excluded so its different then price_unit
|
||||
"UnitPrice": self._l10n_in_round_value(unit_price_in_inr, 3),
|
||||
# total amount is before discount
|
||||
"TotAmt": self._l10n_in_round_value(unit_price_in_inr * line.quantity),
|
||||
"Discount": self._l10n_in_round_value((unit_price_in_inr * line.quantity) * (line.discount / 100)),
|
||||
"TotAmt": self._l10n_in_round_value(unit_price_in_inr * quantity),
|
||||
"Discount": self._l10n_in_round_value((unit_price_in_inr * quantity) * (line.discount / 100)),
|
||||
"AssAmt": self._l10n_in_round_value((sign * line.balance)),
|
||||
"GstRt": self._l10n_in_round_value(tax_details_by_code.get("igst_rate", 0.00) or (
|
||||
tax_details_by_code.get("cgst_rate", 0.00) + tax_details_by_code.get("sgst_rate", 0.00)), 3),
|
||||
@@ -339,6 +354,94 @@ class AccountEdiFormat(models.Model):
|
||||
"TotItemVal": self._l10n_in_round_value(((sign * line.balance) + line_tax_details.get("tax_amount", 0.00))),
|
||||
}
|
||||
|
||||
def _l10n_in_edi_generate_invoice_json_managing_negative_lines(self, invoice, json_payload):
|
||||
"""Set negative lines against positive lines as discount with same HSN code and tax rate
|
||||
|
||||
With negative lines
|
||||
|
||||
product name | hsn code | unit price | qty | discount | total
|
||||
=============================================================
|
||||
product A | 123456 | 1000 | 1 | 100 | 900
|
||||
product B | 123456 | 1500 | 2 | 0 | 3000
|
||||
Discount | 123456 | -300 | 1 | 0 | -300
|
||||
|
||||
Converted to without negative lines
|
||||
|
||||
product name | hsn code | unit price | qty | discount | total
|
||||
=============================================================
|
||||
product A | 123456 | 1000 | 1 | 100 | 900
|
||||
product B | 123456 | 1500 | 2 | 300 | 2700
|
||||
|
||||
totally discounted lines are kept as 0, though
|
||||
"""
|
||||
def discount_group_key(line_vals):
|
||||
return "%s-%s"%(line_vals['HsnCd'], line_vals['GstRt'])
|
||||
|
||||
def put_discount_on(discount_line_vals, other_line_vals):
|
||||
discount = discount_line_vals['AssAmt'] * -1
|
||||
discount_to_allow = other_line_vals['AssAmt']
|
||||
if float_compare(discount_to_allow, discount, precision_rounding=invoice.currency_id.rounding) < 0:
|
||||
# Update discount line, needed when discount is more then max line, in short remaining_discount is not zero
|
||||
discount_line_vals.update({
|
||||
'AssAmt': self._l10n_in_round_value(discount_line_vals['AssAmt'] + other_line_vals['AssAmt']),
|
||||
'IgstAmt': self._l10n_in_round_value(discount_line_vals['IgstAmt'] + other_line_vals['IgstAmt']),
|
||||
'CgstAmt': self._l10n_in_round_value(discount_line_vals['CgstAmt'] + other_line_vals['CgstAmt']),
|
||||
'SgstAmt': self._l10n_in_round_value(discount_line_vals['SgstAmt'] + other_line_vals['SgstAmt']),
|
||||
'CesAmt': self._l10n_in_round_value(discount_line_vals['CesAmt'] + other_line_vals['CesAmt']),
|
||||
'CesNonAdvlAmt': self._l10n_in_round_value(discount_line_vals['CesNonAdvlAmt'] + other_line_vals['CesNonAdvlAmt']),
|
||||
'StateCesAmt': self._l10n_in_round_value(discount_line_vals['StateCesAmt'] + other_line_vals['StateCesAmt']),
|
||||
'StateCesNonAdvlAmt': self._l10n_in_round_value(discount_line_vals['StateCesNonAdvlAmt'] + other_line_vals['StateCesNonAdvlAmt']),
|
||||
'OthChrg': self._l10n_in_round_value(discount_line_vals['OthChrg'] + other_line_vals['OthChrg']),
|
||||
'TotItemVal': self._l10n_in_round_value(discount_line_vals['TotItemVal'] + other_line_vals['TotItemVal']),
|
||||
})
|
||||
other_line_vals.update({
|
||||
'Discount': self._l10n_in_round_value(other_line_vals['Discount'] + discount_to_allow),
|
||||
'AssAmt': 0.00,
|
||||
'IgstAmt': 0.00,
|
||||
'CgstAmt': 0.00,
|
||||
'SgstAmt': 0.00,
|
||||
'CesAmt': 0.00,
|
||||
'CesNonAdvlAmt': 0.00,
|
||||
'StateCesAmt': 0.00,
|
||||
'StateCesNonAdvlAmt': 0.00,
|
||||
'OthChrg': 0.00,
|
||||
'TotItemVal': 0.00,
|
||||
})
|
||||
return False
|
||||
other_line_vals.update({
|
||||
'Discount': self._l10n_in_round_value(other_line_vals['Discount'] + discount),
|
||||
'AssAmt': self._l10n_in_round_value(other_line_vals['AssAmt'] + discount_line_vals['AssAmt']),
|
||||
'IgstAmt': self._l10n_in_round_value(other_line_vals['IgstAmt'] + discount_line_vals['IgstAmt']),
|
||||
'CgstAmt': self._l10n_in_round_value(other_line_vals['CgstAmt'] + discount_line_vals['CgstAmt']),
|
||||
'SgstAmt': self._l10n_in_round_value(other_line_vals['SgstAmt'] + discount_line_vals['SgstAmt']),
|
||||
'CesAmt': self._l10n_in_round_value(other_line_vals['CesAmt'] + discount_line_vals['CesAmt']),
|
||||
'CesNonAdvlAmt': self._l10n_in_round_value(other_line_vals['CesNonAdvlAmt'] + discount_line_vals['CesNonAdvlAmt']),
|
||||
'StateCesAmt': self._l10n_in_round_value(other_line_vals['StateCesAmt'] + discount_line_vals['StateCesAmt']),
|
||||
'StateCesNonAdvlAmt': self._l10n_in_round_value(other_line_vals['StateCesNonAdvlAmt'] + discount_line_vals['StateCesNonAdvlAmt']),
|
||||
'OthChrg': self._l10n_in_round_value(other_line_vals['OthChrg'] + discount_line_vals['OthChrg']),
|
||||
'TotItemVal': self._l10n_in_round_value(other_line_vals['TotItemVal'] + discount_line_vals['TotItemVal']),
|
||||
})
|
||||
return True
|
||||
|
||||
discount_lines = []
|
||||
for discount_line in json_payload['ItemList'].copy(): #to be sure to not skip in the loop:
|
||||
if discount_line['AssAmt'] < 0:
|
||||
discount_lines.append(discount_line)
|
||||
json_payload['ItemList'].remove(discount_line)
|
||||
if not discount_lines:
|
||||
return json_payload
|
||||
|
||||
lines_grouped_and_sorted = defaultdict(list)
|
||||
for line in sorted(json_payload['ItemList'], key=lambda i: i['AssAmt'], reverse=True):
|
||||
lines_grouped_and_sorted[discount_group_key(line)].append(line)
|
||||
|
||||
for discount_line in discount_lines:
|
||||
apply_discount_on_lines = lines_grouped_and_sorted.get(discount_group_key(discount_line), [])
|
||||
for apply_discount_on in apply_discount_on_lines:
|
||||
if put_discount_on(discount_line, apply_discount_on):
|
||||
break
|
||||
return json_payload
|
||||
|
||||
def _l10n_in_edi_generate_invoice_json(self, invoice):
|
||||
tax_details = self._l10n_in_prepare_edi_tax_details(invoice)
|
||||
saler_buyer = self._get_l10n_in_edi_saler_buyer_party(invoice)
|
||||
@@ -418,7 +521,9 @@ class AccountEdiFormat(models.Model):
|
||||
json_payload["ExpDtls"].update({
|
||||
"Port": invoice.l10n_in_shipping_port_code_id.code
|
||||
})
|
||||
return json_payload
|
||||
if not invoice._l10n_in_edi_is_managing_invoice_negative_lines_allowed():
|
||||
return json_payload
|
||||
return self._l10n_in_edi_generate_invoice_json_managing_negative_lines(invoice, json_payload)
|
||||
|
||||
@api.model
|
||||
def _l10n_in_prepare_edi_tax_details(self, move, in_foreign=False):
|
||||
|
||||
@@ -51,3 +51,13 @@ class AccountMove(models.Model):
|
||||
return json.loads(l10n_in_edi.attachment_id.raw.decode("utf-8"))
|
||||
else:
|
||||
return {}
|
||||
|
||||
@api.model
|
||||
def _l10n_in_edi_is_managing_invoice_negative_lines_allowed(self):
|
||||
""" Negative lines are not allowed by the Indian government making some features unavailable like sale_coupon
|
||||
or global discounts. This method allows odoo to distribute the negative discount lines to each others lines
|
||||
with same HSN code making such features available even for Indian people.
|
||||
:return: True if odoo needs to distribute the negative discount lines, False otherwise.
|
||||
"""
|
||||
param_name = 'l10n_in_edi.manage_invoice_negative_lines'
|
||||
return bool(self.env['ir.config_parameter'].sudo().get_param(param_name))
|
||||
|
||||
@@ -10,6 +10,7 @@ class TestEdiJson(AccountTestInvoicingCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls, chart_template_ref="l10n_in.indian_chart_template_standard"):
|
||||
super().setUpClass(chart_template_ref=chart_template_ref)
|
||||
cls.env['ir.config_parameter'].set_param('l10n_in_edi.manage_invoice_negative_lines', True)
|
||||
cls.maxDiff = None
|
||||
cls.company_data["company"].write({
|
||||
"street": "Block no. 401",
|
||||
@@ -31,6 +32,28 @@ class TestEdiJson(AccountTestInvoicingCommon):
|
||||
"l10n_in_gst_treatment": "regular",
|
||||
})
|
||||
cls.product_a.write({"l10n_in_hsn_code": "01111"})
|
||||
cls.product_a2 = cls.env['product.product'].create({
|
||||
'name': 'product_a2',
|
||||
'uom_id': cls.env.ref('uom.product_uom_unit').id,
|
||||
'lst_price': 1000.0,
|
||||
'standard_price': 1000.0,
|
||||
'property_account_income_id': cls.company_data['default_account_revenue'].id,
|
||||
'property_account_expense_id': cls.company_data['default_account_expense'].id,
|
||||
'taxes_id': [(6, 0, cls.tax_sale_a.ids)],
|
||||
'supplier_taxes_id': [(6, 0, cls.tax_purchase_a.ids)],
|
||||
"l10n_in_hsn_code": "01111",
|
||||
})
|
||||
cls.product_a_discount = cls.env['product.product'].create({
|
||||
'name': 'product_a discount',
|
||||
'uom_id': cls.env.ref('uom.product_uom_unit').id,
|
||||
'lst_price': 400.0,
|
||||
'standard_price': 400.0,
|
||||
'property_account_income_id': cls.company_data['default_account_revenue'].id,
|
||||
'property_account_expense_id': cls.company_data['default_account_expense'].id,
|
||||
'taxes_id': [(6, 0, cls.tax_sale_a.ids)],
|
||||
'supplier_taxes_id': [(6, 0, cls.tax_purchase_a.ids)],
|
||||
"l10n_in_hsn_code": "01111",
|
||||
})
|
||||
gst_with_cess = cls.env.ref("l10n_in.%s_sgst_sale_12" % (cls.company_data["company"].id)
|
||||
) + cls.env.ref("l10n_in.%s_cess_5_plus_1591_sale" % (cls.company_data["company"].id))
|
||||
product_with_cess = cls.env["product.product"].create({
|
||||
@@ -56,13 +79,42 @@ class TestEdiJson(AccountTestInvoicingCommon):
|
||||
cls.invoice_zero_qty.write({
|
||||
"invoice_line_ids": [(1, l_id, {"quantity": 0}) for l_id in cls.invoice_zero_qty.invoice_line_ids.ids]})
|
||||
cls.invoice_zero_qty.action_post()
|
||||
cls.invoice_negative_unit_price = cls.init_invoice("out_invoice", post=False, products=cls.product_a + product_with_cess)
|
||||
cls.invoice_negative_unit_price = cls.init_invoice("out_invoice", post=False, products=cls.product_a + cls.product_a_discount + product_with_cess)
|
||||
cls.invoice_negative_unit_price.write({
|
||||
"invoice_line_ids": [
|
||||
(1, cls.invoice_negative_unit_price.invoice_line_ids[0].id, {"price_unit": -400}),
|
||||
(1, cls.invoice_negative_unit_price.invoice_line_ids[1].id, {"price_unit": 1000}),
|
||||
(1, cls.invoice_negative_unit_price.invoice_line_ids[0].id, {"price_unit": 1000}),
|
||||
(1, cls.invoice_negative_unit_price.invoice_line_ids[1].id, {"price_unit": -400}),
|
||||
]})
|
||||
cls.invoice_negative_unit_price.action_post()
|
||||
cls.invoice_negative_qty = cls.init_invoice("out_invoice", post=False, products=cls.product_a + cls.product_a_discount + product_with_cess)
|
||||
cls.invoice_negative_qty.write({
|
||||
"invoice_line_ids": [
|
||||
(1, cls.invoice_negative_qty.invoice_line_ids[0].id, {"price_unit": 1000}),
|
||||
(1, cls.invoice_negative_qty.invoice_line_ids[1].id, {"price_unit": 400, 'quantity': -1}),
|
||||
]})
|
||||
cls.invoice_negative_qty.action_post()
|
||||
cls.invoice_negative_unit_price_and_qty = cls.init_invoice("out_invoice", post=False, products=cls.product_a + cls.product_a_discount + product_with_cess)
|
||||
cls.invoice_negative_unit_price_and_qty.write({
|
||||
"invoice_line_ids": [
|
||||
(1, cls.invoice_negative_unit_price_and_qty.invoice_line_ids[0].id, {"price_unit": -1000, 'quantity': -1}),
|
||||
(1, cls.invoice_negative_unit_price_and_qty.invoice_line_ids[1].id, {"price_unit": -400}),
|
||||
]})
|
||||
cls.invoice_negative_unit_price_and_qty.action_post()
|
||||
cls.invoice_negative_with_discount = cls.init_invoice("out_invoice", post=False, products=cls.product_a + cls.product_a_discount)
|
||||
cls.invoice_negative_with_discount.write({
|
||||
"invoice_line_ids": [
|
||||
(1, cls.invoice_negative_with_discount.invoice_line_ids[0].id, {"price_unit": 2000, 'discount': 50}),
|
||||
(1, cls.invoice_negative_with_discount.invoice_line_ids[1].id, {"price_unit": -400}),
|
||||
]})
|
||||
cls.invoice_negative_with_discount.action_post()
|
||||
cls.invoice_negative_more_than_max_line = cls.init_invoice("out_invoice", post=False, products=cls.product_a + cls.product_a2 + cls.product_a_discount)
|
||||
cls.invoice_negative_more_than_max_line.write({
|
||||
"invoice_line_ids": [
|
||||
(1, cls.invoice_negative_more_than_max_line.invoice_line_ids[0].id, {"price_unit": 2000, 'discount': 50}),
|
||||
(1, cls.invoice_negative_more_than_max_line.invoice_line_ids[1].id, {"price_unit": 1000}),
|
||||
(1, cls.invoice_negative_more_than_max_line.invoice_line_ids[2].id, {"price_unit": -1100}),
|
||||
]})
|
||||
cls.invoice_negative_more_than_max_line.action_post()
|
||||
|
||||
def test_edi_json(self):
|
||||
json_value = self.env["account.edi.format"]._l10n_in_edi_generate_invoice_json(self.invoice)
|
||||
@@ -142,22 +194,74 @@ class TestEdiJson(AccountTestInvoicingCommon):
|
||||
json_value = self.env["account.edi.format"]._l10n_in_edi_generate_invoice_json(self.invoice_negative_unit_price)
|
||||
expected.update({
|
||||
"DocDtls": {"Typ": "INV", "No": "INV/2019/00004", "Dt": "01/01/2019"},
|
||||
"ItemList": [{
|
||||
"SlNo": "1", "PrdDesc": "product_a", "IsServc": "N", "HsnCd": "01111", "Qty": 1.0,
|
||||
"Unit": "UNT", "UnitPrice": -400.0, "TotAmt": -400.0, "Discount": 0.0, "AssAmt": -400.0,
|
||||
"GstRt": 5.0, "IgstAmt": 0.0, "CgstAmt": -10.0, "SgstAmt": -10.0, "CesRt": 0.0, "CesAmt": 0.0,
|
||||
"CesNonAdvlAmt": 0.0, "StateCesRt": 0.0, "StateCesAmt": 0.0, "StateCesNonAdvlAmt": 0.0,
|
||||
"OthChrg": 0.0, "TotItemVal": -420.0
|
||||
}, {
|
||||
"SlNo": "2", "PrdDesc": "product_with_cess", "IsServc": "N", "HsnCd": "02222", "Qty": 1.0,
|
||||
"Unit": "UNT", "UnitPrice": 1000.0, "TotAmt": 1000.0, "Discount": 0.0, "AssAmt": 1000.0,
|
||||
"GstRt": 12.0, "IgstAmt": 0.0, "CgstAmt": 60.0, "SgstAmt": 60.0, "CesRt": 5.0, "CesAmt": 50.0,
|
||||
"CesNonAdvlAmt": 1.59, "StateCesRt": 0.0, "StateCesAmt": 0.0, "StateCesNonAdvlAmt": 0.0,
|
||||
"OthChrg": 0.0, "TotItemVal": 1171.59
|
||||
}],
|
||||
"ItemList": [
|
||||
{
|
||||
"SlNo": "1", "PrdDesc": "product_a", "IsServc": "N", "HsnCd": "01111", "Qty": 1.0,
|
||||
"Unit": "UNT", "UnitPrice": 1000.0, "TotAmt": 1000.0, "Discount": 400.0, "AssAmt": 600.0,
|
||||
"GstRt": 5.0, "IgstAmt": 0.0, "CgstAmt": 15.0, "SgstAmt": 15.0, "CesRt": 0.0, "CesAmt": 0.0,
|
||||
"CesNonAdvlAmt": 0.0, "StateCesRt": 0.0, "StateCesAmt": 0.0, "StateCesNonAdvlAmt": 0.0,
|
||||
"OthChrg": 0.0, "TotItemVal": 630.0
|
||||
},
|
||||
{
|
||||
"SlNo": "3", "PrdDesc": "product_with_cess", "IsServc": "N", "HsnCd": "02222", "Qty": 1.0,
|
||||
"Unit": "UNT", "UnitPrice": 1000.0, "TotAmt": 1000.0, "Discount": 0.0, "AssAmt": 1000.0,
|
||||
"GstRt": 12.0, "IgstAmt": 0.0, "CgstAmt": 60.0, "SgstAmt": 60.0, "CesRt": 5.0, "CesAmt": 50.0,
|
||||
"CesNonAdvlAmt": 1.59, "StateCesRt": 0.0, "StateCesAmt": 0.0, "StateCesNonAdvlAmt": 0.0,
|
||||
"OthChrg": 0.0, "TotItemVal": 1171.59
|
||||
}
|
||||
],
|
||||
"ValDtls": {
|
||||
"AssVal": 600.0, "CgstVal": 50.0, "SgstVal": 50.0, "IgstVal": 0.0, "CesVal": 51.59,
|
||||
"StCesVal": 0.0, "RndOffAmt": 0.0, "TotInvVal": 751.59
|
||||
"AssVal": 1600.0, "CgstVal": 75.0, "SgstVal": 75.0, "IgstVal": 0.0, "CesVal": 51.59,
|
||||
"StCesVal": 0.0, "RndOffAmt": 0.0, "TotInvVal": 1801.59
|
||||
},
|
||||
})
|
||||
self.assertDictEqual(json_value, expected, "Indian EDI with Negative unit price sent json value is not matched")
|
||||
self.assertDictEqual(json_value, expected, "Indian EDI with negative unit price sent json value is not matched")
|
||||
|
||||
expected.update({"DocDtls": {"Typ": "INV", "No": "INV/2019/00005", "Dt": "01/01/2019"}})
|
||||
json_value = self.env["account.edi.format"]._l10n_in_edi_generate_invoice_json(self.invoice_negative_qty)
|
||||
self.assertDictEqual(json_value, expected, "Indian EDI with negative quantity sent json value is not matched")
|
||||
|
||||
expected.update({"DocDtls": {"Typ": "INV", "No": "INV/2019/00006", "Dt": "01/01/2019"}})
|
||||
json_value = self.env["account.edi.format"]._l10n_in_edi_generate_invoice_json(self.invoice_negative_unit_price_and_qty)
|
||||
self.assertDictEqual(json_value, expected, "Indian EDI with negative unit price and quantity sent json value is not matched")
|
||||
|
||||
expected.update({
|
||||
"DocDtls": {"Typ": "INV", "No": "INV/2019/00007", "Dt": "01/01/2019"},
|
||||
"ItemList": [{
|
||||
"SlNo": "1", "PrdDesc": "product_a", "IsServc": "N", "HsnCd": "01111", "Qty": 1.0,
|
||||
"Unit": "UNT", "UnitPrice": 2000.0, "TotAmt": 2000.0, "Discount": 1400.0, "AssAmt": 600.0,
|
||||
"GstRt": 5.0, "IgstAmt": 0.0, "CgstAmt": 15.0, "SgstAmt": 15.0, "CesRt": 0.0, "CesAmt": 0.0,
|
||||
"CesNonAdvlAmt": 0.0, "StateCesRt": 0.0, "StateCesAmt": 0.0, "StateCesNonAdvlAmt": 0.0,
|
||||
"OthChrg": 0.0, "TotItemVal": 630.0
|
||||
}],
|
||||
"ValDtls": {
|
||||
"AssVal": 600.0, "CgstVal": 15.0, "SgstVal": 15.0, "IgstVal": 0.0, "CesVal": 0.0,
|
||||
"StCesVal": 0.0, "RndOffAmt": 0.0, "TotInvVal": 630.0
|
||||
},
|
||||
})
|
||||
json_value = self.env["account.edi.format"]._l10n_in_edi_generate_invoice_json(self.invoice_negative_with_discount)
|
||||
self.assertDictEqual(json_value, expected, "Indian EDI with negative unit price and quantity sent json value is not matched")
|
||||
|
||||
expected.update({
|
||||
"DocDtls": {"Typ": "INV", "No": "INV/2019/00008", "Dt": "01/01/2019"},
|
||||
"ItemList": [{
|
||||
"SlNo": "1", "PrdDesc": "product_a", "IsServc": "N", "HsnCd": "01111", "Qty": 1.0,
|
||||
"Unit": "UNT", "UnitPrice": 2000.0, "TotAmt": 2000.0, "Discount": 2000.0, "AssAmt": 0.0,
|
||||
"GstRt": 5.0, "IgstAmt": 0.0, "CgstAmt": 0.0, "SgstAmt": 0.0, "CesRt": 0.0, "CesAmt": 0.0,
|
||||
"CesNonAdvlAmt": 0.0, "StateCesRt": 0.0, "StateCesAmt": 0.0, "StateCesNonAdvlAmt": 0.0,
|
||||
"OthChrg": 0.0, "TotItemVal": 0.0
|
||||
},
|
||||
{
|
||||
"SlNo": "2", "PrdDesc": "product_a2", "IsServc": "N", "HsnCd": "01111", "Qty": 1.0,
|
||||
"Unit": "UNT", "UnitPrice": 1000.0, "TotAmt": 1000.0, "Discount": 100.0, "AssAmt": 900.0,
|
||||
"GstRt": 5.0, "IgstAmt": 0.0, "CgstAmt": 22.5, "SgstAmt": 22.5, "CesRt": 0.0, "CesAmt": 0.0,
|
||||
"CesNonAdvlAmt": 0.0, "StateCesRt": 0.0, "StateCesAmt": 0.0, "StateCesNonAdvlAmt": 0.0,
|
||||
"OthChrg": 0.0, "TotItemVal": 945.0
|
||||
}],
|
||||
"ValDtls": {
|
||||
"AssVal": 900.0, "CgstVal": 22.5, "SgstVal": 22.5, "IgstVal": 0.0, "CesVal": 0.0,
|
||||
"StCesVal": 0.0, "RndOffAmt": 0.0, "TotInvVal": 945.0
|
||||
},
|
||||
})
|
||||
json_value = self.env['account.edi.format']._l10n_in_edi_generate_invoice_json(self.invoice_negative_more_than_max_line)
|
||||
self.assertDictEqual(json_value, expected, "Indian EDI with negative value more than max line sent json value is not matched")
|
||||
|
||||
Reference in New Issue
Block a user