Skip to content
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
2 changes: 1 addition & 1 deletion csf_za/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.2.3"
__version__ = "0.2.4"
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ def test_process_gl_entries(self, mock_cached_value, mock_transform, mock_get_ca
"voucher": frappe._dict(
{
"voucher_type": "Sales Invoice",
"account": "VAT Account",
"general_ledger_debit": 0,
"general_ledger_credit": 15,
"sales_invoice_taxes_total": 115,
Expand All @@ -147,6 +148,7 @@ def test_process_gl_entries(self, mock_cached_value, mock_transform, mock_get_ca
"voucher": frappe._dict(
{
"voucher_type": "Purchase Invoice",
"account": "VAT Account",
"general_ledger_debit": 15,
"general_ledger_credit": 0,
"purchase_invoice_taxes_total": 115,
Expand All @@ -160,6 +162,7 @@ def test_process_gl_entries(self, mock_cached_value, mock_transform, mock_get_ca
"voucher": frappe._dict(
{
"voucher_type": "Sales Invoice",
"account": "VAT Account",
"general_ledger_debit": 0,
"general_ledger_credit": 15,
"sales_invoice_taxes_total": -115,
Expand All @@ -173,6 +176,7 @@ def test_process_gl_entries(self, mock_cached_value, mock_transform, mock_get_ca
"voucher": frappe._dict(
{
"voucher_type": "Purchase Invoice",
"account": "VAT Account",
"general_ledger_debit": 15,
"general_ledger_credit": 0,
"purchase_invoice_taxes_total": -115,
Expand All @@ -186,6 +190,7 @@ def test_process_gl_entries(self, mock_cached_value, mock_transform, mock_get_ca
"voucher": frappe._dict(
{
"voucher_type": "Journal Entry",
"account": "VAT Account",
"general_ledger_debit": 15,
"general_ledger_credit": 0,
}
Expand Down Expand Up @@ -220,6 +225,7 @@ def test_process_gl_entries(self, mock_cached_value, mock_transform, mock_get_ca
"voucher": frappe._dict(
{
"voucher_type": "Journal Entry",
"account": "VAT Account",
"general_ledger_debit": -15,
"general_ledger_credit": 0,
}
Expand Down Expand Up @@ -254,6 +260,7 @@ def test_process_gl_entries(self, mock_cached_value, mock_transform, mock_get_ca
"voucher": frappe._dict(
{
"voucher_type": "Journal Entry",
"account": "VAT Account",
"general_ledger_debit": -15,
"general_ledger_credit": 0,
"is_cancelled": 1,
Expand Down Expand Up @@ -311,3 +318,63 @@ def test_process_gl_entries(self, mock_cached_value, mock_transform, mock_get_ca
self.assertIsNone(results[6].classification)
self.assertIsNone(results[6].tax_amount)
self.assertIsNone(results[6].incl_tax_amount)

@patch(
"csf_za.tax_compliance.doctype.value_added_tax_return.value_added_tax_return.frappe.get_cached_doc"
)
@patch(
"csf_za.tax_compliance.doctype.value_added_tax_return.value_added_tax_return.transform_gl_entries"
)
@patch(
"csf_za.tax_compliance.doctype.value_added_tax_return.value_added_tax_return.frappe.get_cached_value"
)
@patch(
"csf_za.tax_compliance.doctype.value_added_tax_return.value_added_tax_return.VAT_RETURN_SETTING_FIELD_MAP",
[
{
"field_name": "zero_rated_vat_field",
"classification": "Output - C Zero Rated (excl goods exported)",
"reference_doctype": "Sales Invoice",
}
],
)
def test_process_gl_entries_with_zero_rate_invoice(
self, mock_cached_value, mock_transform, mock_get_cached_doc
):
mock_settings = frappe._dict(
{
"tax_accounts": [frappe._dict({"account": "VAT Account"})],
"zero_rated_vat_field": "Zero Rated VAT Template",
}
)
mock_vouchers = frappe._dict(
{
"SI-003": frappe._dict(
{
"voucher": frappe._dict(
{
"voucher_type": "Sales Invoice",
"account": "Debtors", # Not a tax account
"general_ledger_debit": 100,
"general_ledger_credit": 0,
"sales_invoice_taxes_total": 100,
"taxes_and_charges_template": "Zero Rated VAT Template",
"is_cancelled": 0,
}
)
}
)
}
)

mock_get_cached_doc.return_value = mock_settings
mock_transform.return_value = mock_vouchers
mock_cached_value.side_effect = lambda doctype, docname, fieldname: "Classified"

vat_return = frappe.new_doc("Value-added Tax Return")
results = vat_return.process_gl_entries([])

self.assertEqual(len(results), 1)
self.assertEqual(results[0].classification, "Output - C Zero Rated (excl goods exported)")
self.assertEqual(results[0].tax_amount, 0)
self.assertEqual(results[0].incl_tax_amount, 100)
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ def get_gl_entries(self):
.on((pitc.parent == pi.name) & (pitc.account_head == gle.account))
.select(
gle.name,
gle.account,
gle.voucher_type,
gle.voucher_no,
gle.posting_date,
Expand All @@ -236,7 +237,22 @@ def get_gl_entries(self):
.where(
(gle.posting_date >= self.date_from)
& (gle.posting_date <= self.date_to)
& (gle.account.isin(tax_accounts))
& (
gle.account.isin(tax_accounts)
| (
(gle.voucher_type == "Sales Invoice")
& si.taxes_and_charges.isnotnull()
& si.debit_to.isnotnull()
& (gle.account == si.debit_to)
)
# Exclude Purchase Invoices with 0 tax for now
# | (
# (gle.voucher_type == "Purchase Invoice")
# & pi.taxes_and_charges.isnotnull()
# & pi.credit_to.isnotnull()
# & (gle.account == pi.credit_to)
# )
)
)
)

Expand All @@ -261,7 +277,7 @@ def process_gl_entries(self, gl_entries):
entry for entry in VAT_RETURN_SETTING_FIELD_MAP if vat_return_settings.get(entry["field_name"])
]

vouchers = transform_gl_entries(gl_entries)
vouchers = transform_gl_entries(gl_entries, tax_accounts)

for voucher_no, item in vouchers.items():
voucher = item.voucher
Expand All @@ -270,20 +286,27 @@ def process_gl_entries(self, gl_entries):
if voucher.is_cancelled:
continue

voucher.tax_amount = voucher.general_ledger_debit or voucher.general_ledger_credit
if hasattr(voucher, "account") and voucher.account in tax_accounts:
voucher.tax_amount = voucher.general_ledger_debit or voucher.general_ledger_credit
else:
voucher.tax_amount = 0

voucher.classification_debugging = "🚀"
if voucher.voucher_type in ("Sales Invoice", "Purchase Invoice"):
voucher.incl_tax_amount = (
voucher.sales_invoice_taxes_total or voucher.purchase_invoice_taxes_total
)

# For vouchers with zero-rated tax (aka tax amount = 0), set the total amount
if not voucher.incl_tax_amount:
voucher.incl_tax_amount = voucher.general_ledger_debit or voucher.general_ledger_credit

# If the voucher_type is a reversal (e.g. Credit and Debit Notes, change the sign of tax_amount)
if voucher.incl_tax_amount < 0 and voucher.tax_amount > 0:
voucher.tax_amount = voucher.tax_amount * -1

voucher.classification_debugging += (
"\n🚀 voucher_type is a 'Sales Invoice' or 'Purchase Invoice')"
"\n🚀 voucher_type is a 'Sales Invoice' or 'Purchase Invoice'"
)
voucher.classification_debugging += (
f"\n🚀 taxes_and_charges_template = '{voucher.taxes_and_charges_template}'"
Expand Down Expand Up @@ -414,7 +437,7 @@ def process_gl_entries(self, gl_entries):
return [voucher.voucher for voucher in vouchers.values()]


def transform_gl_entries(gl_entries):
def transform_gl_entries(gl_entries, tax_accounts):
"""
Transform flat list of entries to a dict with voucher_no as key
E.g.
Expand Down Expand Up @@ -461,8 +484,19 @@ def transform_gl_entries(gl_entries):
"""
vouchers = {}
for entry in gl_entries:
vouchers.setdefault(entry.name, frappe._dict({"voucher": entry, "linked_journal_entries": []}))[
"linked_journal_entries"
].append(entry)
voucher_no = entry.voucher_no
if voucher_no not in vouchers:
vouchers[voucher_no] = frappe._dict({"voucher": entry, "linked_journal_entries": []})
else:
# If the new entry is from a tax account, and the old one is not, then it becomes the main voucher
if (
hasattr(entry, "account")
and hasattr(vouchers[voucher_no].voucher, "account")
and entry.account in tax_accounts
and vouchers[voucher_no].voucher.account not in tax_accounts
):
vouchers[voucher_no].voucher = entry

vouchers[voucher_no]["linked_journal_entries"].append(entry)

return vouchers
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@
},
{
"field_name": "standard_rate_capital",
"classification": "Output - B Standard rate (only capital goods) ",
"classification": "Output - B Standard rate (only capital goods)",
"reference_doctype": "Sales Invoice",
},
{
"field_name": "zero_rate_non_exported",
"classification": "Output - C Zero Rated (excl goods exported) ",
"classification": "Output - C Zero Rated (excl goods exported)",
"reference_doctype": "Sales Invoice",
},
{
"field_name": "zero_rate_exported",
"classification": "Output - D Zero Rated (only goods exported) ",
"classification": "Output - D Zero Rated (only goods exported)",
"reference_doctype": "Sales Invoice",
},
{
Expand Down