mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-03 12:19:12 +00:00
Merge pull request #45004 from frappe/version-14-hotfix
chore: release v14
This commit is contained in:
@@ -12,6 +12,7 @@ from frappe.utils import cint, flt
|
|||||||
|
|
||||||
from erpnext import get_default_cost_center
|
from erpnext import get_default_cost_center
|
||||||
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_total_allocated_amount
|
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_total_allocated_amount
|
||||||
|
from erpnext.accounts.party import get_party_account
|
||||||
from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import (
|
from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import (
|
||||||
get_amounts_not_reflected_in_system,
|
get_amounts_not_reflected_in_system,
|
||||||
get_entries,
|
get_entries,
|
||||||
@@ -284,54 +285,56 @@ def create_payment_entry_bts(
|
|||||||
bank_transaction = frappe.db.get_values(
|
bank_transaction = frappe.db.get_values(
|
||||||
"Bank Transaction",
|
"Bank Transaction",
|
||||||
bank_transaction_name,
|
bank_transaction_name,
|
||||||
fieldname=["name", "unallocated_amount", "deposit", "bank_account"],
|
fieldname=["name", "unallocated_amount", "deposit", "bank_account", "currency"],
|
||||||
as_dict=True,
|
as_dict=True,
|
||||||
)[0]
|
)[0]
|
||||||
paid_amount = bank_transaction.unallocated_amount
|
|
||||||
payment_type = "Receive" if bank_transaction.deposit > 0.0 else "Pay"
|
payment_type = "Receive" if bank_transaction.deposit > 0.0 else "Pay"
|
||||||
|
|
||||||
company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
|
bank_account = frappe.get_cached_value("Bank Account", bank_transaction.bank_account, "account")
|
||||||
company = frappe.get_value("Account", company_account, "company")
|
company = frappe.get_cached_value("Account", bank_account, "company")
|
||||||
payment_entry_dict = {
|
party_account = get_party_account(party_type, party, company)
|
||||||
"company": company,
|
|
||||||
"payment_type": payment_type,
|
|
||||||
"reference_no": reference_number,
|
|
||||||
"reference_date": reference_date,
|
|
||||||
"party_type": party_type,
|
|
||||||
"party": party,
|
|
||||||
"posting_date": posting_date,
|
|
||||||
"paid_amount": paid_amount,
|
|
||||||
"received_amount": paid_amount,
|
|
||||||
}
|
|
||||||
payment_entry = frappe.new_doc("Payment Entry")
|
|
||||||
|
|
||||||
payment_entry.update(payment_entry_dict)
|
bank_currency = bank_transaction.currency
|
||||||
|
party_currency = frappe.get_cached_value("Account", party_account, "account_currency")
|
||||||
|
|
||||||
if mode_of_payment:
|
exc_rate = get_exchange_rate(bank_currency, party_currency, posting_date)
|
||||||
payment_entry.mode_of_payment = mode_of_payment
|
|
||||||
if project:
|
|
||||||
payment_entry.project = project
|
|
||||||
if cost_center:
|
|
||||||
payment_entry.cost_center = cost_center
|
|
||||||
if payment_type == "Receive":
|
|
||||||
payment_entry.paid_to = company_account
|
|
||||||
else:
|
|
||||||
payment_entry.paid_from = company_account
|
|
||||||
|
|
||||||
payment_entry.validate()
|
amt_in_bank_acc_currency = bank_transaction.unallocated_amount
|
||||||
|
amount_in_party_currency = bank_transaction.unallocated_amount * exc_rate
|
||||||
|
|
||||||
|
pe = frappe.new_doc("Payment Entry")
|
||||||
|
pe.payment_type = payment_type
|
||||||
|
pe.company = company
|
||||||
|
pe.reference_no = reference_number
|
||||||
|
pe.reference_date = reference_date
|
||||||
|
pe.party_type = party_type
|
||||||
|
pe.party = party
|
||||||
|
pe.posting_date = posting_date
|
||||||
|
pe.paid_from = party_account if payment_type == "Receive" else bank_account
|
||||||
|
pe.paid_to = party_account if payment_type == "Pay" else bank_account
|
||||||
|
pe.paid_from_account_currency = party_currency if payment_type == "Receive" else bank_currency
|
||||||
|
pe.paid_to_account_currency = party_currency if payment_type == "Pay" else bank_currency
|
||||||
|
pe.paid_amount = amount_in_party_currency if payment_type == "Receive" else amt_in_bank_acc_currency
|
||||||
|
pe.received_amount = amount_in_party_currency if payment_type == "Pay" else amt_in_bank_acc_currency
|
||||||
|
pe.mode_of_payment = mode_of_payment
|
||||||
|
pe.project = project
|
||||||
|
pe.cost_center = cost_center
|
||||||
|
|
||||||
|
pe.validate()
|
||||||
|
|
||||||
if allow_edit:
|
if allow_edit:
|
||||||
return payment_entry
|
return pe
|
||||||
|
|
||||||
payment_entry.insert()
|
pe.insert()
|
||||||
|
pe.submit()
|
||||||
|
|
||||||
payment_entry.submit()
|
|
||||||
vouchers = json.dumps(
|
vouchers = json.dumps(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"payment_doctype": "Payment Entry",
|
"payment_doctype": "Payment Entry",
|
||||||
"payment_name": payment_entry.name,
|
"payment_name": pe.name,
|
||||||
"amount": paid_amount,
|
"amount": amt_in_bank_acc_currency,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -455,8 +458,12 @@ def get_linked_payments(
|
|||||||
def subtract_allocations(gl_account, vouchers):
|
def subtract_allocations(gl_account, vouchers):
|
||||||
"Look up & subtract any existing Bank Transaction allocations"
|
"Look up & subtract any existing Bank Transaction allocations"
|
||||||
copied = []
|
copied = []
|
||||||
|
|
||||||
|
voucher_docs = [(voucher[1], voucher[2]) for voucher in vouchers]
|
||||||
|
voucher_allocated_amounts = get_total_allocated_amount(voucher_docs)
|
||||||
|
|
||||||
for voucher in vouchers:
|
for voucher in vouchers:
|
||||||
rows = get_total_allocated_amount(voucher[1], voucher[2])
|
rows = voucher_allocated_amounts.get((voucher[1], voucher[2])) or []
|
||||||
amount = None
|
amount = None
|
||||||
for row in rows:
|
for row in rows:
|
||||||
if row["gl_account"] == gl_account:
|
if row["gl_account"] == gl_account:
|
||||||
|
|||||||
@@ -93,10 +93,16 @@ class BankTransaction(StatusUpdater):
|
|||||||
- clear means: set the latest transaction date as clearance date
|
- clear means: set the latest transaction date as clearance date
|
||||||
"""
|
"""
|
||||||
remaining_amount = self.unallocated_amount
|
remaining_amount = self.unallocated_amount
|
||||||
|
payment_entry_docs = [(pe.payment_document, pe.payment_entry) for pe in self.payment_entries]
|
||||||
|
pe_bt_allocations = get_total_allocated_amount(payment_entry_docs)
|
||||||
|
|
||||||
for payment_entry in self.payment_entries:
|
for payment_entry in self.payment_entries:
|
||||||
if payment_entry.allocated_amount == 0.0:
|
if payment_entry.allocated_amount == 0.0:
|
||||||
unallocated_amount, should_clear, latest_transaction = get_clearance_details(
|
unallocated_amount, should_clear, latest_transaction = get_clearance_details(
|
||||||
self, payment_entry
|
self,
|
||||||
|
payment_entry,
|
||||||
|
pe_bt_allocations.get((payment_entry.payment_document, payment_entry.payment_entry))
|
||||||
|
or [],
|
||||||
)
|
)
|
||||||
|
|
||||||
if 0.0 == unallocated_amount:
|
if 0.0 == unallocated_amount:
|
||||||
@@ -182,7 +188,7 @@ def get_doctypes_for_bank_reconciliation():
|
|||||||
return frappe.get_hooks("bank_reconciliation_doctypes")
|
return frappe.get_hooks("bank_reconciliation_doctypes")
|
||||||
|
|
||||||
|
|
||||||
def get_clearance_details(transaction, payment_entry):
|
def get_clearance_details(transaction, payment_entry, bt_allocations):
|
||||||
"""
|
"""
|
||||||
There should only be one bank gle for a voucher.
|
There should only be one bank gle for a voucher.
|
||||||
Could be none for a Bank Transaction.
|
Could be none for a Bank Transaction.
|
||||||
@@ -191,7 +197,6 @@ def get_clearance_details(transaction, payment_entry):
|
|||||||
"""
|
"""
|
||||||
gl_bank_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
|
gl_bank_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
|
||||||
gles = get_related_bank_gl_entries(payment_entry.payment_document, payment_entry.payment_entry)
|
gles = get_related_bank_gl_entries(payment_entry.payment_document, payment_entry.payment_entry)
|
||||||
bt_allocations = get_total_allocated_amount(payment_entry.payment_document, payment_entry.payment_entry)
|
|
||||||
|
|
||||||
unallocated_amount = min(
|
unallocated_amount = min(
|
||||||
transaction.unallocated_amount,
|
transaction.unallocated_amount,
|
||||||
@@ -247,44 +252,52 @@ def get_related_bank_gl_entries(doctype, docname):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def get_total_allocated_amount(doctype, docname):
|
def get_total_allocated_amount(docs):
|
||||||
"""
|
"""
|
||||||
Gets the sum of allocations for a voucher on each bank GL account
|
Gets the sum of allocations for a voucher on each bank GL account
|
||||||
along with the latest bank transaction name & date
|
along with the latest bank transaction name & date
|
||||||
NOTE: query may also include just saved vouchers/payments but with zero allocated_amount
|
NOTE: query may also include just saved vouchers/payments but with zero allocated_amount
|
||||||
"""
|
"""
|
||||||
|
if not docs:
|
||||||
|
return {}
|
||||||
|
|
||||||
# nosemgrep: frappe-semgrep-rules.rules.frappe-using-db-sql
|
# nosemgrep: frappe-semgrep-rules.rules.frappe-using-db-sql
|
||||||
result = frappe.db.sql(
|
result = frappe.db.sql(
|
||||||
"""
|
"""
|
||||||
SELECT total, latest_name, latest_date, gl_account FROM (
|
SELECT total, latest_name, latest_date, gl_account, payment_document, payment_entry FROM (
|
||||||
SELECT
|
SELECT
|
||||||
ROW_NUMBER() OVER w AS rownum,
|
ROW_NUMBER() OVER w AS rownum,
|
||||||
SUM(btp.allocated_amount) OVER(PARTITION BY ba.account) AS total,
|
SUM(btp.allocated_amount) OVER(PARTITION BY ba.account, btp.payment_document, btp.payment_entry) AS total,
|
||||||
FIRST_VALUE(bt.name) OVER w AS latest_name,
|
FIRST_VALUE(bt.name) OVER w AS latest_name,
|
||||||
FIRST_VALUE(bt.date) OVER w AS latest_date,
|
FIRST_VALUE(bt.date) OVER w AS latest_date,
|
||||||
ba.account AS gl_account
|
ba.account AS gl_account,
|
||||||
|
btp.payment_document,
|
||||||
|
btp.payment_entry
|
||||||
FROM
|
FROM
|
||||||
`tabBank Transaction Payments` btp
|
`tabBank Transaction Payments` btp
|
||||||
LEFT JOIN `tabBank Transaction` bt ON bt.name=btp.parent
|
LEFT JOIN `tabBank Transaction` bt ON bt.name=btp.parent
|
||||||
LEFT JOIN `tabBank Account` ba ON ba.name=bt.bank_account
|
LEFT JOIN `tabBank Account` ba ON ba.name=bt.bank_account
|
||||||
WHERE
|
WHERE
|
||||||
btp.payment_document = %(doctype)s
|
(btp.payment_document, btp.payment_entry) IN %(docs)s
|
||||||
AND btp.payment_entry = %(docname)s
|
|
||||||
AND bt.docstatus = 1
|
AND bt.docstatus = 1
|
||||||
WINDOW w AS (PARTITION BY ba.account ORDER BY bt.date desc)
|
WINDOW w AS (PARTITION BY ba.account, btp.payment_document, btp.payment_entry ORDER BY bt.date DESC)
|
||||||
) temp
|
) temp
|
||||||
WHERE
|
WHERE
|
||||||
rownum = 1
|
rownum = 1
|
||||||
""",
|
""",
|
||||||
dict(doctype=doctype, docname=docname),
|
dict(docs=docs),
|
||||||
as_dict=True,
|
as_dict=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
payment_allocation_details = {}
|
||||||
for row in result:
|
for row in result:
|
||||||
# Why is this *sometimes* a byte string?
|
# Why is this *sometimes* a byte string?
|
||||||
if isinstance(row["latest_name"], bytes):
|
if isinstance(row["latest_name"], bytes):
|
||||||
row["latest_name"] = row["latest_name"].decode()
|
row["latest_name"] = row["latest_name"].decode()
|
||||||
row["latest_date"] = frappe.utils.getdate(row["latest_date"])
|
row["latest_date"] = frappe.utils.getdate(row["latest_date"])
|
||||||
return result
|
payment_allocation_details.setdefault((row["payment_document"], row["payment_entry"]), []).append(row)
|
||||||
|
|
||||||
|
return payment_allocation_details
|
||||||
|
|
||||||
|
|
||||||
def get_paid_amount(payment_entry, currency, gl_bank_account):
|
def get_paid_amount(payment_entry, currency, gl_bank_account):
|
||||||
|
|||||||
@@ -366,6 +366,8 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
|
|||||||
hide_fields(this.frm.doc);
|
hide_fields(this.frm.doc);
|
||||||
if(cint(this.frm.doc.is_paid)) {
|
if(cint(this.frm.doc.is_paid)) {
|
||||||
this.frm.set_value("allocate_advances_automatically", 0);
|
this.frm.set_value("allocate_advances_automatically", 0);
|
||||||
|
this.frm.set_value("payment_terms_template", "");
|
||||||
|
this.frm.set_value("payment_schedule", []);
|
||||||
if(!this.frm.doc.company) {
|
if(!this.frm.doc.company) {
|
||||||
this.frm.set_value("is_paid", 0)
|
this.frm.set_value("is_paid", 0)
|
||||||
frappe.msgprint(__("Please specify Company to proceed"));
|
frappe.msgprint(__("Please specify Company to proceed"));
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry import (
|
|||||||
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
|
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
|
||||||
create_stock_reconciliation,
|
create_stock_reconciliation,
|
||||||
)
|
)
|
||||||
|
from erpnext.stock.get_item_details import get_item_tax_map
|
||||||
from erpnext.stock.utils import get_incoming_rate, get_stock_balance
|
from erpnext.stock.utils import get_incoming_rate, get_stock_balance
|
||||||
|
|
||||||
|
|
||||||
@@ -2817,13 +2818,26 @@ class TestSalesInvoice(FrappeTestCase):
|
|||||||
item.save()
|
item.save()
|
||||||
|
|
||||||
sales_invoice = create_sales_invoice(item="T Shirt", rate=700, do_not_submit=True)
|
sales_invoice = create_sales_invoice(item="T Shirt", rate=700, do_not_submit=True)
|
||||||
|
item_tax_map = get_item_tax_map(
|
||||||
|
company=sales_invoice.company,
|
||||||
|
item_tax_template=sales_invoice.items[0].item_tax_template,
|
||||||
|
)
|
||||||
|
|
||||||
self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC")
|
self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC")
|
||||||
|
self.assertEqual(sales_invoice.items[0].item_tax_rate, item_tax_map)
|
||||||
|
|
||||||
# Apply discount
|
# Apply discount
|
||||||
sales_invoice.apply_discount_on = "Net Total"
|
sales_invoice.apply_discount_on = "Net Total"
|
||||||
sales_invoice.discount_amount = 300
|
sales_invoice.discount_amount = 300
|
||||||
sales_invoice.save()
|
sales_invoice.save()
|
||||||
|
|
||||||
|
item_tax_map = get_item_tax_map(
|
||||||
|
company=sales_invoice.company,
|
||||||
|
item_tax_template=sales_invoice.items[0].item_tax_template,
|
||||||
|
)
|
||||||
|
|
||||||
self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC")
|
self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC")
|
||||||
|
self.assertEqual(sales_invoice.items[0].item_tax_rate, item_tax_map)
|
||||||
|
|
||||||
@change_settings("Selling Settings", {"enable_discount_accounting": 1})
|
@change_settings("Selling Settings", {"enable_discount_accounting": 1})
|
||||||
def test_sales_invoice_with_discount_accounting_enabled(self):
|
def test_sales_invoice_with_discount_accounting_enabled(self):
|
||||||
|
|||||||
@@ -1,840 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"account_manager": null,
|
|
||||||
"accounts": [],
|
|
||||||
"companies": [],
|
|
||||||
"credit_limits": [],
|
|
||||||
"customer_details": null,
|
|
||||||
"customer_group": "All Customer Groups",
|
|
||||||
"customer_name": "_Test Customer",
|
|
||||||
"customer_pos_id": null,
|
|
||||||
"customer_primary_address": null,
|
|
||||||
"customer_primary_contact": null,
|
|
||||||
"customer_type": "Company",
|
|
||||||
"default_bank_account": null,
|
|
||||||
"default_commission_rate": 0.0,
|
|
||||||
"default_currency": null,
|
|
||||||
"default_price_list": null,
|
|
||||||
"default_sales_partner": null,
|
|
||||||
"disabled": 0,
|
|
||||||
"dn_required": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "Customer",
|
|
||||||
"email_id": null,
|
|
||||||
"gender": null,
|
|
||||||
"image": null,
|
|
||||||
"industry": null,
|
|
||||||
"is_frozen": 0,
|
|
||||||
"is_internal_customer": 0,
|
|
||||||
"language": "en",
|
|
||||||
"lead_name": null,
|
|
||||||
"loyalty_program": null,
|
|
||||||
"loyalty_program_tier": null,
|
|
||||||
"market_segment": null,
|
|
||||||
"mobile_no": null,
|
|
||||||
"modified": "2021-02-15 05:18:03.624724",
|
|
||||||
"name": "_Test Customer",
|
|
||||||
"naming_series": "CUST-.YYYY.-",
|
|
||||||
"pan": null,
|
|
||||||
"parent": null,
|
|
||||||
"parentfield": null,
|
|
||||||
"parenttype": null,
|
|
||||||
"payment_terms": null,
|
|
||||||
"primary_address": null,
|
|
||||||
"represents_company": "",
|
|
||||||
"sales_team": [],
|
|
||||||
"salutation": null,
|
|
||||||
"so_required": 0,
|
|
||||||
"tax_category": null,
|
|
||||||
"tax_id": null,
|
|
||||||
"tax_withholding_category": null,
|
|
||||||
"territory": "All Territories",
|
|
||||||
"website": null
|
|
||||||
},{
|
|
||||||
"accounts": [],
|
|
||||||
"allow_purchase_invoice_creation_without_purchase_order": 0,
|
|
||||||
"allow_purchase_invoice_creation_without_purchase_receipt": 0,
|
|
||||||
"companies": [],
|
|
||||||
"country": "United Kingdom",
|
|
||||||
"default_bank_account": null,
|
|
||||||
"default_currency": null,
|
|
||||||
"default_price_list": null,
|
|
||||||
"disabled": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "Supplier",
|
|
||||||
"hold_type": "",
|
|
||||||
"image": null,
|
|
||||||
"is_frozen": 0,
|
|
||||||
"is_internal_supplier": 0,
|
|
||||||
"is_transporter": 0,
|
|
||||||
"language": "en",
|
|
||||||
"modified": "2021-03-31 16:47:10.109316",
|
|
||||||
"name": "_Test Supplier",
|
|
||||||
"naming_series": "SUP-.YYYY.-",
|
|
||||||
"on_hold": 0,
|
|
||||||
"pan": null,
|
|
||||||
"parent": null,
|
|
||||||
"parentfield": null,
|
|
||||||
"parenttype": null,
|
|
||||||
"payment_terms": null,
|
|
||||||
"prevent_pos": 0,
|
|
||||||
"prevent_rfqs": 0,
|
|
||||||
"release_date": null,
|
|
||||||
"represents_company": null,
|
|
||||||
"supplier_details": null,
|
|
||||||
"supplier_group": "Raw Material",
|
|
||||||
"supplier_name": "_Test Supplier",
|
|
||||||
"supplier_type": "Company",
|
|
||||||
"tax_category": null,
|
|
||||||
"tax_id": null,
|
|
||||||
"tax_withholding_category": null,
|
|
||||||
"warn_pos": 0,
|
|
||||||
"warn_rfqs": 0,
|
|
||||||
"website": null
|
|
||||||
},{
|
|
||||||
"account_currency": "GBP",
|
|
||||||
"account_name": "Debtors",
|
|
||||||
"account_number": "",
|
|
||||||
"account_type": "Receivable",
|
|
||||||
"balance_must_be": "",
|
|
||||||
"company": "_T",
|
|
||||||
"disabled": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "Account",
|
|
||||||
"freeze_account": "No",
|
|
||||||
"include_in_gross": 0,
|
|
||||||
"inter_company_account": 0,
|
|
||||||
"is_group": 0,
|
|
||||||
"lft": 58,
|
|
||||||
"modified": "2021-03-26 04:44:19.955468",
|
|
||||||
"name": "Debtors - _T",
|
|
||||||
"old_parent": null,
|
|
||||||
"parent": null,
|
|
||||||
"parent_account": "Application of Funds (Assets) - _T",
|
|
||||||
"parentfield": null,
|
|
||||||
"parenttype": null,
|
|
||||||
"report_type": "Balance Sheet",
|
|
||||||
"rgt": 59,
|
|
||||||
"root_type": "Asset",
|
|
||||||
"tax_rate": 0.0
|
|
||||||
},{
|
|
||||||
"account_currency": "GBP",
|
|
||||||
"account_name": "Sales",
|
|
||||||
"account_number": "",
|
|
||||||
"account_type": "Income Account",
|
|
||||||
"balance_must_be": "",
|
|
||||||
"company": "_T",
|
|
||||||
"disabled": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "Account",
|
|
||||||
"freeze_account": "No",
|
|
||||||
"include_in_gross": 0,
|
|
||||||
"inter_company_account": 0,
|
|
||||||
"is_group": 0,
|
|
||||||
"lft": 291,
|
|
||||||
"modified": "2021-03-26 04:50:21.697703",
|
|
||||||
"name": "Sales - _T",
|
|
||||||
"old_parent": null,
|
|
||||||
"parent": null,
|
|
||||||
"parent_account": "Income - _T",
|
|
||||||
"parentfield": null,
|
|
||||||
"parenttype": null,
|
|
||||||
"report_type": "Profit and Loss",
|
|
||||||
"rgt": 292,
|
|
||||||
"root_type": "Income",
|
|
||||||
"tax_rate": 0.0
|
|
||||||
},{
|
|
||||||
"account_currency": "GBP",
|
|
||||||
"account_name": "VAT on Sales",
|
|
||||||
"account_number": "",
|
|
||||||
"account_type": "Tax",
|
|
||||||
"balance_must_be": "",
|
|
||||||
"company": "_T",
|
|
||||||
"disabled": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "Account",
|
|
||||||
"freeze_account": "No",
|
|
||||||
"include_in_gross": 0,
|
|
||||||
"inter_company_account": 0,
|
|
||||||
"is_group": 0,
|
|
||||||
"lft": 317,
|
|
||||||
"modified": "2021-03-26 04:50:21.697703",
|
|
||||||
"name": "VAT on Sales - _T",
|
|
||||||
"old_parent": null,
|
|
||||||
"parent": null,
|
|
||||||
"parent_account": "Source of Funds (Liabilities) - _T",
|
|
||||||
"parentfield": null,
|
|
||||||
"parenttype": null,
|
|
||||||
"report_type": "Balance Sheet",
|
|
||||||
"rgt": 318,
|
|
||||||
"root_type": "Liability",
|
|
||||||
"tax_rate": 0.0
|
|
||||||
},{
|
|
||||||
"account_currency": "GBP",
|
|
||||||
"account_name": "Cost of Goods Sold",
|
|
||||||
"account_number": "",
|
|
||||||
"account_type": "Cost of Goods Sold",
|
|
||||||
"balance_must_be": "",
|
|
||||||
"company": "_T",
|
|
||||||
"disabled": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "Account",
|
|
||||||
"freeze_account": "No",
|
|
||||||
"include_in_gross": 0,
|
|
||||||
"inter_company_account": 0,
|
|
||||||
"is_group": 0,
|
|
||||||
"lft": 171,
|
|
||||||
"modified": "2021-03-26 04:44:19.994857",
|
|
||||||
"name": "Cost of Goods Sold - _T",
|
|
||||||
"old_parent": null,
|
|
||||||
"parent": null,
|
|
||||||
"parent_account": "Expenses - _T",
|
|
||||||
"parentfield": null,
|
|
||||||
"parenttype": null,
|
|
||||||
"report_type": "Profit and Loss",
|
|
||||||
"rgt": 172,
|
|
||||||
"root_type": "Expense",
|
|
||||||
"tax_rate": 0.0
|
|
||||||
},{
|
|
||||||
"account_currency": "GBP",
|
|
||||||
"account_name": "VAT on Purchases",
|
|
||||||
"account_number": "",
|
|
||||||
"account_type": "Tax",
|
|
||||||
"balance_must_be": "",
|
|
||||||
"company": "_T",
|
|
||||||
"disabled": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "Account",
|
|
||||||
"freeze_account": "No",
|
|
||||||
"include_in_gross": 0,
|
|
||||||
"inter_company_account": 0,
|
|
||||||
"is_group": 0,
|
|
||||||
"lft": 80,
|
|
||||||
"modified": "2021-03-26 04:44:19.961983",
|
|
||||||
"name": "VAT on Purchases - _T",
|
|
||||||
"old_parent": null,
|
|
||||||
"parent": null,
|
|
||||||
"parent_account": "Application of Funds (Assets) - _T",
|
|
||||||
"parentfield": null,
|
|
||||||
"parenttype": null,
|
|
||||||
"report_type": "Balance Sheet",
|
|
||||||
"rgt": 81,
|
|
||||||
"root_type": "Asset",
|
|
||||||
"tax_rate": 0.0
|
|
||||||
},{
|
|
||||||
"account_currency": "GBP",
|
|
||||||
"account_name": "Creditors",
|
|
||||||
"account_number": "",
|
|
||||||
"account_type": "Payable",
|
|
||||||
"balance_must_be": "",
|
|
||||||
"company": "_T",
|
|
||||||
"disabled": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "Account",
|
|
||||||
"freeze_account": "No",
|
|
||||||
"include_in_gross": 0,
|
|
||||||
"inter_company_account": 0,
|
|
||||||
"is_group": 0,
|
|
||||||
"lft": 302,
|
|
||||||
"modified": "2021-03-26 04:50:21.697703",
|
|
||||||
"name": "Creditors - _T",
|
|
||||||
"old_parent": null,
|
|
||||||
"parent": null,
|
|
||||||
"parent_account": "Source of Funds (Liabilities) - _T",
|
|
||||||
"parentfield": null,
|
|
||||||
"parenttype": null,
|
|
||||||
"report_type": "Balance Sheet",
|
|
||||||
"rgt": 303,
|
|
||||||
"root_type": "Liability",
|
|
||||||
"tax_rate": 0.0
|
|
||||||
},{
|
|
||||||
"additional_discount_percentage": 0.0,
|
|
||||||
"address_display": null,
|
|
||||||
"adjust_advance_taxes": 0,
|
|
||||||
"advances": [],
|
|
||||||
"against_expense_account": "Cost of Goods Sold - _T",
|
|
||||||
"allocate_advances_automatically": 0,
|
|
||||||
"amended_from": null,
|
|
||||||
"apply_discount_on": "Grand Total",
|
|
||||||
"apply_tds": 0,
|
|
||||||
"auto_repeat": null,
|
|
||||||
"base_discount_amount": 0.0,
|
|
||||||
"base_grand_total": 511.68,
|
|
||||||
"base_in_words": "GBP Five Hundred And Eleven and Sixty Eight Pence only.",
|
|
||||||
"base_net_total": 426.4,
|
|
||||||
"base_paid_amount": 0.0,
|
|
||||||
"base_rounded_total": 511.68,
|
|
||||||
"base_rounding_adjustment": 0.0,
|
|
||||||
"base_taxes_and_charges_added": 85.28,
|
|
||||||
"base_taxes_and_charges_deducted": 0.0,
|
|
||||||
"base_total": 426.4,
|
|
||||||
"base_total_taxes_and_charges": 85.28,
|
|
||||||
"base_write_off_amount": 0.0,
|
|
||||||
"bill_date": null,
|
|
||||||
"bill_no": null,
|
|
||||||
"billing_address": null,
|
|
||||||
"billing_address_display": null,
|
|
||||||
"buying_price_list": "Standard Buying",
|
|
||||||
"cash_bank_account": null,
|
|
||||||
"clearance_date": null,
|
|
||||||
"company": "_T",
|
|
||||||
"contact_display": null,
|
|
||||||
"contact_email": null,
|
|
||||||
"contact_mobile": null,
|
|
||||||
"contact_person": null,
|
|
||||||
"conversion_rate": 1.0,
|
|
||||||
"cost_center": null,
|
|
||||||
"credit_to": "Creditors - _T",
|
|
||||||
"currency": "GBP",
|
|
||||||
"disable_rounded_total": 0,
|
|
||||||
"discount_amount": 0.0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "Purchase Invoice",
|
|
||||||
"due_date": null,
|
|
||||||
"from_date": null,
|
|
||||||
"grand_total": 511.68,
|
|
||||||
"group_same_items": 0,
|
|
||||||
"hold_comment": null,
|
|
||||||
"ignore_pricing_rule": 0,
|
|
||||||
"in_words": "GBP Five Hundred And Eleven and Sixty Eight Pence only.",
|
|
||||||
"inter_company_invoice_reference": null,
|
|
||||||
"is_internal_supplier": 0,
|
|
||||||
"is_opening": "No",
|
|
||||||
"is_paid": 0,
|
|
||||||
"is_return": 0,
|
|
||||||
"is_subcontracted": 0,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"allow_zero_valuation_rate": 0,
|
|
||||||
"amount": 426.4,
|
|
||||||
"asset_category": null,
|
|
||||||
"asset_location": null,
|
|
||||||
"base_amount": 426.4,
|
|
||||||
"base_net_amount": 426.4,
|
|
||||||
"base_net_rate": 5.33,
|
|
||||||
"base_price_list_rate": 5.33,
|
|
||||||
"base_rate": 5.33,
|
|
||||||
"base_rate_with_margin": 0.0,
|
|
||||||
"batch_no": null,
|
|
||||||
"bom": null,
|
|
||||||
"brand": null,
|
|
||||||
"conversion_factor": 0.0,
|
|
||||||
"cost_center": "Main - _T",
|
|
||||||
"deferred_expense_account": null,
|
|
||||||
"description": "<div class=\"ql-editor read-mode\"><p>Fluid to make widgets</p></div>",
|
|
||||||
"discount_amount": 0.0,
|
|
||||||
"discount_percentage": 0.0,
|
|
||||||
"enable_deferred_expense": 0,
|
|
||||||
"expense_account": "Cost of Goods Sold - _T",
|
|
||||||
"from_warehouse": null,
|
|
||||||
"image": null,
|
|
||||||
"include_exploded_items": 0,
|
|
||||||
"is_fixed_asset": 0,
|
|
||||||
"is_free_item": 0,
|
|
||||||
"item_code": null,
|
|
||||||
"item_group": null,
|
|
||||||
"item_name": "Widget Fluid 1Litre",
|
|
||||||
"item_tax_amount": 0.0,
|
|
||||||
"item_tax_rate": "{\"VAT on Purchases - _T\": 20.0}",
|
|
||||||
"item_tax_template": null,
|
|
||||||
"landed_cost_voucher_amount": 0.0,
|
|
||||||
"manufacturer": null,
|
|
||||||
"manufacturer_part_no": null,
|
|
||||||
"margin_rate_or_amount": 0.0,
|
|
||||||
"margin_type": "",
|
|
||||||
"net_amount": 426.4,
|
|
||||||
"net_rate": 5.33,
|
|
||||||
"page_break": 0,
|
|
||||||
"parent": null,
|
|
||||||
"parentfield": "items",
|
|
||||||
"parenttype": "Purchase Invoice",
|
|
||||||
"po_detail": null,
|
|
||||||
"pr_detail": null,
|
|
||||||
"price_list_rate": 5.33,
|
|
||||||
"pricing_rules": null,
|
|
||||||
"project": null,
|
|
||||||
"purchase_invoice_item": null,
|
|
||||||
"purchase_order": null,
|
|
||||||
"purchase_receipt": null,
|
|
||||||
"qty": 80.0,
|
|
||||||
"quality_inspection": null,
|
|
||||||
"rate": 5.33,
|
|
||||||
"rate_with_margin": 0.0,
|
|
||||||
"received_qty": 0.0,
|
|
||||||
"rejected_qty": 0.0,
|
|
||||||
"rejected_serial_no": null,
|
|
||||||
"rejected_warehouse": null,
|
|
||||||
"rm_supp_cost": 0.0,
|
|
||||||
"sales_invoice_item": null,
|
|
||||||
"serial_no": null,
|
|
||||||
"service_end_date": null,
|
|
||||||
"service_start_date": null,
|
|
||||||
"service_stop_date": null,
|
|
||||||
"stock_qty": 0.0,
|
|
||||||
"stock_uom": "Nos",
|
|
||||||
"stock_uom_rate": 0.0,
|
|
||||||
"total_weight": 0.0,
|
|
||||||
"uom": "Nos",
|
|
||||||
"valuation_rate": 0.0,
|
|
||||||
"warehouse": null,
|
|
||||||
"weight_per_unit": 0.0,
|
|
||||||
"weight_uom": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"language": "en",
|
|
||||||
"letter_head": null,
|
|
||||||
"mode_of_payment": null,
|
|
||||||
"modified": "2021-04-03 03:33:09.180453",
|
|
||||||
"name": null,
|
|
||||||
"naming_series": "ACC-PINV-.YYYY.-",
|
|
||||||
"net_total": 426.4,
|
|
||||||
"on_hold": 0,
|
|
||||||
"other_charges_calculation": "<div class=\"tax-break-up\" style=\"overflow-x: auto;\">\n\t<table class=\"table table-bordered table-hover\">\n\t\t<thead>\n\t\t\t<tr>\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-left\">Item</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">Taxable Amount</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">VAT on Purchases</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t</tr>\n\t\t</thead>\n\t\t<tbody>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Widget Fluid 1Litre</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 426.40\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(20.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 85.28\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t</tbody>\n\t</table>\n</div>",
|
|
||||||
"outstanding_amount": 511.68,
|
|
||||||
"paid_amount": 0.0,
|
|
||||||
"parent": null,
|
|
||||||
"parentfield": null,
|
|
||||||
"parenttype": null,
|
|
||||||
"party_account_currency": "GBP",
|
|
||||||
"payment_schedule": [],
|
|
||||||
"payment_terms_template": null,
|
|
||||||
"plc_conversion_rate": 1.0,
|
|
||||||
"posting_date": null,
|
|
||||||
"posting_time": "16:59:56.789522",
|
|
||||||
"price_list_currency": "GBP",
|
|
||||||
"pricing_rules": [],
|
|
||||||
"project": null,
|
|
||||||
"rejected_warehouse": null,
|
|
||||||
"release_date": null,
|
|
||||||
"remarks": "No Remarks",
|
|
||||||
"represents_company": null,
|
|
||||||
"return_against": null,
|
|
||||||
"rounded_total": 511.68,
|
|
||||||
"rounding_adjustment": 0.0,
|
|
||||||
"scan_barcode": null,
|
|
||||||
"select_print_heading": null,
|
|
||||||
"set_from_warehouse": null,
|
|
||||||
"set_posting_time": 0,
|
|
||||||
"set_warehouse": null,
|
|
||||||
"shipping_address": null,
|
|
||||||
"shipping_address_display": "",
|
|
||||||
"shipping_rule": null,
|
|
||||||
"status": "Unpaid",
|
|
||||||
"supplied_items": [],
|
|
||||||
"supplier": "_Test Supplier",
|
|
||||||
"supplier_address": null,
|
|
||||||
"supplier_name": "_Test Supplier",
|
|
||||||
"supplier_warehouse": "Stores - _T",
|
|
||||||
"tax_category": null,
|
|
||||||
"tax_id": null,
|
|
||||||
"tax_withholding_category": null,
|
|
||||||
"taxes": [
|
|
||||||
{
|
|
||||||
"account_head": "VAT on Purchases - _T",
|
|
||||||
"add_deduct_tax": "Add",
|
|
||||||
"base_tax_amount": 85.28,
|
|
||||||
"base_tax_amount_after_discount_amount": 85.28,
|
|
||||||
"base_total": 511.68,
|
|
||||||
"category": "Total",
|
|
||||||
"charge_type": "On Net Total",
|
|
||||||
"cost_center": "Main - _T",
|
|
||||||
"description": "VAT on Purchases",
|
|
||||||
"included_in_print_rate": 0,
|
|
||||||
"item_wise_tax_detail": "{\"Widget Fluid 1Litre\":[20.0,85.28]}",
|
|
||||||
"parent": null,
|
|
||||||
"parentfield": "taxes",
|
|
||||||
"parenttype": "Purchase Invoice",
|
|
||||||
"rate": 0.0,
|
|
||||||
"row_id": null,
|
|
||||||
"tax_amount": 85.28,
|
|
||||||
"tax_amount_after_discount_amount": 85.28,
|
|
||||||
"total": 511.68
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"taxes_and_charges": null,
|
|
||||||
"taxes_and_charges_added": 85.28,
|
|
||||||
"taxes_and_charges_deducted": 0.0,
|
|
||||||
"tc_name": null,
|
|
||||||
"terms": null,
|
|
||||||
"title": "_Purchase Invoice",
|
|
||||||
"to_date": null,
|
|
||||||
"total": 426.4,
|
|
||||||
"total_advance": 0.0,
|
|
||||||
"total_net_weight": 0.0,
|
|
||||||
"total_qty": 80.0,
|
|
||||||
"total_taxes_and_charges": 85.28,
|
|
||||||
"unrealized_profit_loss_account": null,
|
|
||||||
"update_stock": 0,
|
|
||||||
"write_off_account": null,
|
|
||||||
"write_off_amount": 0.0,
|
|
||||||
"write_off_cost_center": null
|
|
||||||
},{
|
|
||||||
"account_for_change_amount": null,
|
|
||||||
"additional_discount_percentage": 0.0,
|
|
||||||
"address_display": null,
|
|
||||||
"advances": [],
|
|
||||||
"against_income_account": "Sales - _T",
|
|
||||||
"allocate_advances_automatically": 0,
|
|
||||||
"amended_from": null,
|
|
||||||
"apply_discount_on": "Grand Total",
|
|
||||||
"auto_repeat": null,
|
|
||||||
"base_change_amount": 0.0,
|
|
||||||
"base_discount_amount": 0.0,
|
|
||||||
"base_grand_total": 868.25,
|
|
||||||
"base_in_words": "GBP Eight Hundred And Sixty Eight and Twenty Five Pence only.",
|
|
||||||
"base_net_total": 825.0,
|
|
||||||
"base_paid_amount": 0.0,
|
|
||||||
"base_rounded_total": 868.25,
|
|
||||||
"base_rounding_adjustment": 0.0,
|
|
||||||
"base_total": 825.0,
|
|
||||||
"base_total_taxes_and_charges": 43.25,
|
|
||||||
"base_write_off_amount": 0.0,
|
|
||||||
"c_form_applicable": "No",
|
|
||||||
"c_form_no": null,
|
|
||||||
"campaign": null,
|
|
||||||
"cash_bank_account": null,
|
|
||||||
"change_amount": 0.0,
|
|
||||||
"commission_rate": 0.0,
|
|
||||||
"company": "_T",
|
|
||||||
"company_address": null,
|
|
||||||
"company_address_display": null,
|
|
||||||
"company_tax_id": null,
|
|
||||||
"contact_display": null,
|
|
||||||
"contact_email": null,
|
|
||||||
"contact_mobile": null,
|
|
||||||
"contact_person": null,
|
|
||||||
"conversion_rate": 1.0,
|
|
||||||
"cost_center": null,
|
|
||||||
"currency": "GBP",
|
|
||||||
"customer": "_Test Customer",
|
|
||||||
"customer_address": null,
|
|
||||||
"customer_group": "All Customer Groups",
|
|
||||||
"customer_name": "_Test Customer",
|
|
||||||
"debit_to": "Debtors - _T",
|
|
||||||
"discount_amount": 0.0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "Sales Invoice",
|
|
||||||
"due_date": null,
|
|
||||||
"from_date": null,
|
|
||||||
"grand_total": 868.25,
|
|
||||||
"group_same_items": 0,
|
|
||||||
"ignore_pricing_rule": 0,
|
|
||||||
"in_words": "GBP Eight Hundred And Sixty Eight and Twenty Five Pence only.",
|
|
||||||
"inter_company_invoice_reference": null,
|
|
||||||
"is_consolidated": 0,
|
|
||||||
"is_discounted": 0,
|
|
||||||
"is_internal_customer": 0,
|
|
||||||
"is_opening": "No",
|
|
||||||
"is_pos": 0,
|
|
||||||
"is_return": 0,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"actual_batch_qty": 0.0,
|
|
||||||
"actual_qty": 0.0,
|
|
||||||
"allow_zero_valuation_rate": 0,
|
|
||||||
"amount": 200.0,
|
|
||||||
"asset": null,
|
|
||||||
"barcode": null,
|
|
||||||
"base_amount": 200.0,
|
|
||||||
"base_net_amount": 200.0,
|
|
||||||
"base_net_rate": 50.0,
|
|
||||||
"base_price_list_rate": 0.0,
|
|
||||||
"base_rate": 50.0,
|
|
||||||
"base_rate_with_margin": 0.0,
|
|
||||||
"batch_no": null,
|
|
||||||
"brand": null,
|
|
||||||
"conversion_factor": 1.0,
|
|
||||||
"cost_center": "Main - _T",
|
|
||||||
"customer_item_code": null,
|
|
||||||
"deferred_revenue_account": null,
|
|
||||||
"delivered_by_supplier": 0,
|
|
||||||
"delivered_qty": 0.0,
|
|
||||||
"delivery_note": null,
|
|
||||||
"description": "<div class=\"ql-editor read-mode\"><p>Used</p></div>",
|
|
||||||
"discount_amount": 0.0,
|
|
||||||
"discount_percentage": 0.0,
|
|
||||||
"dn_detail": null,
|
|
||||||
"enable_deferred_revenue": 0,
|
|
||||||
"expense_account": null,
|
|
||||||
"finance_book": null,
|
|
||||||
"image": null,
|
|
||||||
"income_account": "Sales - _T",
|
|
||||||
"incoming_rate": 0.0,
|
|
||||||
"is_fixed_asset": 0,
|
|
||||||
"is_free_item": 0,
|
|
||||||
"item_code": null,
|
|
||||||
"item_group": null,
|
|
||||||
"item_name": "Dunlop tyres",
|
|
||||||
"item_tax_rate": "{\"VAT on Sales - _T\": 20.0}",
|
|
||||||
"item_tax_template": null,
|
|
||||||
"margin_rate_or_amount": 0.0,
|
|
||||||
"margin_type": "",
|
|
||||||
"net_amount": 200.0,
|
|
||||||
"net_rate": 50.0,
|
|
||||||
"page_break": 0,
|
|
||||||
"parent": null,
|
|
||||||
"parentfield": "items",
|
|
||||||
"parenttype": "Sales Invoice",
|
|
||||||
"price_list_rate": 0.0,
|
|
||||||
"pricing_rules": null,
|
|
||||||
"project": null,
|
|
||||||
"qty": 4.0,
|
|
||||||
"quality_inspection": null,
|
|
||||||
"rate": 50.0,
|
|
||||||
"rate_with_margin": 0.0,
|
|
||||||
"sales_invoice_item": null,
|
|
||||||
"sales_order": null,
|
|
||||||
"serial_no": null,
|
|
||||||
"service_end_date": null,
|
|
||||||
"service_start_date": null,
|
|
||||||
"service_stop_date": null,
|
|
||||||
"so_detail": null,
|
|
||||||
"stock_qty": 4.0,
|
|
||||||
"stock_uom": "Nos",
|
|
||||||
"stock_uom_rate": 50.0,
|
|
||||||
"target_warehouse": null,
|
|
||||||
"total_weight": 0.0,
|
|
||||||
"uom": "Nos",
|
|
||||||
"warehouse": null,
|
|
||||||
"weight_per_unit": 0.0,
|
|
||||||
"weight_uom": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"actual_batch_qty": 0.0,
|
|
||||||
"actual_qty": 0.0,
|
|
||||||
"allow_zero_valuation_rate": 0,
|
|
||||||
"amount": 65.0,
|
|
||||||
"asset": null,
|
|
||||||
"barcode": null,
|
|
||||||
"base_amount": 65.0,
|
|
||||||
"base_net_amount": 65.0,
|
|
||||||
"base_net_rate": 65.0,
|
|
||||||
"base_price_list_rate": 0.0,
|
|
||||||
"base_rate": 65.0,
|
|
||||||
"base_rate_with_margin": 0.0,
|
|
||||||
"batch_no": null,
|
|
||||||
"brand": null,
|
|
||||||
"conversion_factor": 1.0,
|
|
||||||
"cost_center": "Main - _T",
|
|
||||||
"customer_item_code": null,
|
|
||||||
"deferred_revenue_account": null,
|
|
||||||
"delivered_by_supplier": 0,
|
|
||||||
"delivered_qty": 0.0,
|
|
||||||
"delivery_note": null,
|
|
||||||
"description": "<div class=\"ql-editor read-mode\"><p>Used</p></div>",
|
|
||||||
"discount_amount": 0.0,
|
|
||||||
"discount_percentage": 0.0,
|
|
||||||
"dn_detail": null,
|
|
||||||
"enable_deferred_revenue": 0,
|
|
||||||
"expense_account": null,
|
|
||||||
"finance_book": null,
|
|
||||||
"image": null,
|
|
||||||
"income_account": "Sales - _T",
|
|
||||||
"incoming_rate": 0.0,
|
|
||||||
"is_fixed_asset": 0,
|
|
||||||
"is_free_item": 0,
|
|
||||||
"item_code": "",
|
|
||||||
"item_group": null,
|
|
||||||
"item_name": "Continental tyres",
|
|
||||||
"item_tax_rate": "{\"VAT on Sales - _T\": 5.0}",
|
|
||||||
"item_tax_template": null,
|
|
||||||
"margin_rate_or_amount": 0.0,
|
|
||||||
"margin_type": "",
|
|
||||||
"net_amount": 65.0,
|
|
||||||
"net_rate": 65.0,
|
|
||||||
"page_break": 0,
|
|
||||||
"parent": null,
|
|
||||||
"parentfield": "items",
|
|
||||||
"parenttype": "Sales Invoice",
|
|
||||||
"price_list_rate": 0.0,
|
|
||||||
"pricing_rules": null,
|
|
||||||
"project": null,
|
|
||||||
"qty": 1.0,
|
|
||||||
"quality_inspection": null,
|
|
||||||
"rate": 65.0,
|
|
||||||
"rate_with_margin": 0.0,
|
|
||||||
"sales_invoice_item": null,
|
|
||||||
"sales_order": null,
|
|
||||||
"serial_no": null,
|
|
||||||
"service_end_date": null,
|
|
||||||
"service_start_date": null,
|
|
||||||
"service_stop_date": null,
|
|
||||||
"so_detail": null,
|
|
||||||
"stock_qty": 1.0,
|
|
||||||
"stock_uom": null,
|
|
||||||
"stock_uom_rate": 65.0,
|
|
||||||
"target_warehouse": null,
|
|
||||||
"total_weight": 0.0,
|
|
||||||
"uom": "Nos",
|
|
||||||
"warehouse": null,
|
|
||||||
"weight_per_unit": 0.0,
|
|
||||||
"weight_uom": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"actual_batch_qty": 0.0,
|
|
||||||
"actual_qty": 0.0,
|
|
||||||
"allow_zero_valuation_rate": 0,
|
|
||||||
"amount": 560.0,
|
|
||||||
"asset": null,
|
|
||||||
"barcode": null,
|
|
||||||
"base_amount": 560.0,
|
|
||||||
"base_net_amount": 560.0,
|
|
||||||
"base_net_rate": 70.0,
|
|
||||||
"base_price_list_rate": 0.0,
|
|
||||||
"base_rate": 70.0,
|
|
||||||
"base_rate_with_margin": 0.0,
|
|
||||||
"batch_no": null,
|
|
||||||
"brand": null,
|
|
||||||
"conversion_factor": 1.0,
|
|
||||||
"cost_center": "Main - _T",
|
|
||||||
"customer_item_code": null,
|
|
||||||
"deferred_revenue_account": null,
|
|
||||||
"delivered_by_supplier": 0,
|
|
||||||
"delivered_qty": 0.0,
|
|
||||||
"delivery_note": null,
|
|
||||||
"description": "<div class=\"ql-editor read-mode\"><p>New</p></div>",
|
|
||||||
"discount_amount": 0.0,
|
|
||||||
"discount_percentage": 0.0,
|
|
||||||
"dn_detail": null,
|
|
||||||
"enable_deferred_revenue": 0,
|
|
||||||
"expense_account": null,
|
|
||||||
"finance_book": null,
|
|
||||||
"image": null,
|
|
||||||
"income_account": "Sales - _T",
|
|
||||||
"incoming_rate": 0.0,
|
|
||||||
"is_fixed_asset": 0,
|
|
||||||
"is_free_item": 0,
|
|
||||||
"item_code": null,
|
|
||||||
"item_group": null,
|
|
||||||
"item_name": "Toyo tyres",
|
|
||||||
"item_tax_rate": "{\"VAT on Sales - _T\": 0.0}",
|
|
||||||
"item_tax_template": null,
|
|
||||||
"margin_rate_or_amount": 0.0,
|
|
||||||
"margin_type": "",
|
|
||||||
"net_amount": 560.0,
|
|
||||||
"net_rate": 70.0,
|
|
||||||
"page_break": 0,
|
|
||||||
"parent": null,
|
|
||||||
"parentfield": "items",
|
|
||||||
"parenttype": "Sales Invoice",
|
|
||||||
"price_list_rate": 0.0,
|
|
||||||
"pricing_rules": null,
|
|
||||||
"project": null,
|
|
||||||
"qty": 8.0,
|
|
||||||
"quality_inspection": null,
|
|
||||||
"rate": 70.0,
|
|
||||||
"rate_with_margin": 0.0,
|
|
||||||
"sales_invoice_item": null,
|
|
||||||
"sales_order": null,
|
|
||||||
"serial_no": null,
|
|
||||||
"service_end_date": null,
|
|
||||||
"service_start_date": null,
|
|
||||||
"service_stop_date": null,
|
|
||||||
"so_detail": null,
|
|
||||||
"stock_qty": 8.0,
|
|
||||||
"stock_uom": null,
|
|
||||||
"stock_uom_rate": 70.0,
|
|
||||||
"target_warehouse": null,
|
|
||||||
"total_weight": 0.0,
|
|
||||||
"uom": "Nos",
|
|
||||||
"warehouse": null,
|
|
||||||
"weight_per_unit": 0.0,
|
|
||||||
"weight_uom": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"language": "en",
|
|
||||||
"letter_head": null,
|
|
||||||
"loyalty_amount": 0.0,
|
|
||||||
"loyalty_points": 0,
|
|
||||||
"loyalty_program": null,
|
|
||||||
"loyalty_redemption_account": null,
|
|
||||||
"loyalty_redemption_cost_center": null,
|
|
||||||
"modified": "2021-02-16 05:18:59.755144",
|
|
||||||
"name": null,
|
|
||||||
"naming_series": "ACC-SINV-.YYYY.-",
|
|
||||||
"net_total": 825.0,
|
|
||||||
"other_charges_calculation": "<div class=\"tax-break-up\" style=\"overflow-x: auto;\">\n\t<table class=\"table table-bordered table-hover\">\n\t\t<thead>\n\t\t\t<tr>\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-left\">Item</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">Taxable Amount</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">VAT on Sales</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t</tr>\n\t\t</thead>\n\t\t<tbody>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Dunlop tyres</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 200.00\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(20.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 40.00\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Continental tyres</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 65.00\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(5.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 3.25\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Toyo tyres</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 560.00\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(0.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 0.00\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t</tbody>\n\t</table>\n</div>",
|
|
||||||
"outstanding_amount": 868.25,
|
|
||||||
"packed_items": [],
|
|
||||||
"paid_amount": 0.0,
|
|
||||||
"parent": null,
|
|
||||||
"parentfield": null,
|
|
||||||
"parenttype": null,
|
|
||||||
"party_account_currency": "GBP",
|
|
||||||
"payment_schedule": [],
|
|
||||||
"payment_terms_template": null,
|
|
||||||
"payments": [],
|
|
||||||
"plc_conversion_rate": 1.0,
|
|
||||||
"po_date": null,
|
|
||||||
"po_no": "",
|
|
||||||
"pos_profile": null,
|
|
||||||
"posting_date": null,
|
|
||||||
"posting_time": "5:19:02.994077",
|
|
||||||
"price_list_currency": "GBP",
|
|
||||||
"pricing_rules": [],
|
|
||||||
"project": null,
|
|
||||||
"redeem_loyalty_points": 0,
|
|
||||||
"remarks": "No Remarks",
|
|
||||||
"represents_company": "",
|
|
||||||
"return_against": null,
|
|
||||||
"rounded_total": 868.25,
|
|
||||||
"rounding_adjustment": 0.0,
|
|
||||||
"sales_partner": null,
|
|
||||||
"sales_team": [],
|
|
||||||
"scan_barcode": null,
|
|
||||||
"select_print_heading": null,
|
|
||||||
"selling_price_list": "Standard Selling",
|
|
||||||
"set_posting_time": 0,
|
|
||||||
"set_target_warehouse": null,
|
|
||||||
"set_warehouse": null,
|
|
||||||
"shipping_address": null,
|
|
||||||
"shipping_address_name": "",
|
|
||||||
"shipping_rule": null,
|
|
||||||
"source": null,
|
|
||||||
"status": "Overdue",
|
|
||||||
"tax_category": "",
|
|
||||||
"tax_id": null,
|
|
||||||
"taxes": [
|
|
||||||
{
|
|
||||||
"account_head": "VAT on Sales - _T",
|
|
||||||
"base_tax_amount": 43.25,
|
|
||||||
"base_tax_amount_after_discount_amount": 43.25,
|
|
||||||
"base_total": 868.25,
|
|
||||||
"charge_type": "On Net Total",
|
|
||||||
"cost_center": "Main - _T",
|
|
||||||
"description": "VAT on Sales",
|
|
||||||
"included_in_print_rate": 0,
|
|
||||||
"item_wise_tax_detail": "{\"Dunlop tyres\":[20.0,40.0],\"Continental tyres\":[5.0,3.25],\"Toyo tyres\":[0.0,0.0]}",
|
|
||||||
"parent": null,
|
|
||||||
"parentfield": "taxes",
|
|
||||||
"parenttype": "Sales Invoice",
|
|
||||||
"rate": 0.0,
|
|
||||||
"row_id": null,
|
|
||||||
"tax_amount": 43.25,
|
|
||||||
"tax_amount_after_discount_amount": 43.25,
|
|
||||||
"total": 868.25
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"taxes_and_charges": null,
|
|
||||||
"tc_name": null,
|
|
||||||
"terms": null,
|
|
||||||
"territory": "All Territories",
|
|
||||||
"timesheets": [],
|
|
||||||
"title": "_Sales Invoice",
|
|
||||||
"to_date": null,
|
|
||||||
"total": 825.0,
|
|
||||||
"total_advance": 0.0,
|
|
||||||
"total_billing_amount": 0.0,
|
|
||||||
"total_commission": 0.0,
|
|
||||||
"total_net_weight": 0.0,
|
|
||||||
"total_qty": 13.0,
|
|
||||||
"total_taxes_and_charges": 43.25,
|
|
||||||
"unrealized_profit_loss_account": null,
|
|
||||||
"update_billed_amount_in_sales_order": 0,
|
|
||||||
"update_stock": 0,
|
|
||||||
"write_off_account": null,
|
|
||||||
"write_off_amount": 0.0,
|
|
||||||
"write_off_cost_center": null,
|
|
||||||
"write_off_outstanding_amount_automatically": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
import datetime
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
|
||||||
from frappe.utils import (
|
|
||||||
add_to_date,
|
|
||||||
get_first_day,
|
|
||||||
get_last_day,
|
|
||||||
get_year_ending,
|
|
||||||
get_year_start,
|
|
||||||
getdate,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .tax_detail import filter_match, save_custom_report
|
|
||||||
|
|
||||||
|
|
||||||
class TestTaxDetail(unittest.TestCase):
|
|
||||||
def load_testdocs(self):
|
|
||||||
from erpnext.accounts.utils import FiscalYearError, get_fiscal_year
|
|
||||||
|
|
||||||
datapath, _ = os.path.splitext(os.path.realpath(__file__))
|
|
||||||
with open(datapath + ".json") as fp:
|
|
||||||
docs = json.load(fp)
|
|
||||||
|
|
||||||
now = getdate()
|
|
||||||
self.from_date = get_first_day(now)
|
|
||||||
self.to_date = get_last_day(now)
|
|
||||||
|
|
||||||
try:
|
|
||||||
get_fiscal_year(now, company="_T")
|
|
||||||
except FiscalYearError:
|
|
||||||
docs = [
|
|
||||||
{
|
|
||||||
"companies": [
|
|
||||||
{
|
|
||||||
"company": "_T",
|
|
||||||
"parent": "_Test Fiscal",
|
|
||||||
"parentfield": "companies",
|
|
||||||
"parenttype": "Fiscal Year",
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"doctype": "Fiscal Year",
|
|
||||||
"year": "_Test Fiscal",
|
|
||||||
"year_end_date": get_year_ending(now),
|
|
||||||
"year_start_date": get_year_start(now),
|
|
||||||
},
|
|
||||||
*docs,
|
|
||||||
]
|
|
||||||
|
|
||||||
docs = [
|
|
||||||
{
|
|
||||||
"abbr": "_T",
|
|
||||||
"company_name": "_T",
|
|
||||||
"country": "United Kingdom",
|
|
||||||
"default_currency": "GBP",
|
|
||||||
"doctype": "Company",
|
|
||||||
"name": "_T",
|
|
||||||
},
|
|
||||||
*docs,
|
|
||||||
]
|
|
||||||
|
|
||||||
for doc in docs:
|
|
||||||
try:
|
|
||||||
db_doc = frappe.get_doc(doc)
|
|
||||||
if "Invoice" in db_doc.doctype:
|
|
||||||
db_doc.due_date = add_to_date(now, days=1)
|
|
||||||
db_doc.insert()
|
|
||||||
# Create GL Entries:
|
|
||||||
db_doc.submit()
|
|
||||||
else:
|
|
||||||
db_doc.insert(ignore_if_duplicate=True)
|
|
||||||
except frappe.exceptions.DuplicateEntryError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def load_defcols(self):
|
|
||||||
self.company = frappe.get_doc("Company", "_T")
|
|
||||||
custom_report = frappe.get_doc("Report", "Tax Detail")
|
|
||||||
self.default_columns, _ = custom_report.run_query_report(
|
|
||||||
filters={
|
|
||||||
"from_date": "2021-03-01",
|
|
||||||
"to_date": "2021-03-31",
|
|
||||||
"company": self.company.name,
|
|
||||||
"mode": "run",
|
|
||||||
"report_name": "Tax Detail",
|
|
||||||
},
|
|
||||||
user=frappe.session.user,
|
|
||||||
)
|
|
||||||
|
|
||||||
def rm_testdocs(self):
|
|
||||||
"Remove the Company and all data"
|
|
||||||
from erpnext.setup.doctype.company.company import create_transaction_deletion_request
|
|
||||||
|
|
||||||
create_transaction_deletion_request(self.company.name)
|
|
||||||
|
|
||||||
def test_report(self):
|
|
||||||
self.load_testdocs()
|
|
||||||
self.load_defcols()
|
|
||||||
report_name = save_custom_report(
|
|
||||||
"Tax Detail",
|
|
||||||
"_Test Tax Detail",
|
|
||||||
json.dumps(
|
|
||||||
{
|
|
||||||
"columns": self.default_columns,
|
|
||||||
"sections": {
|
|
||||||
"Box1": {"Filter0": {"type": "filter", "filters": {"4": "VAT on Sales"}}},
|
|
||||||
"Box2": {"Filter0": {"type": "filter", "filters": {"4": "Acquisition"}}},
|
|
||||||
"Box3": {"Box1": {"type": "section"}, "Box2": {"type": "section"}},
|
|
||||||
"Box4": {"Filter0": {"type": "filter", "filters": {"4": "VAT on Purchases"}}},
|
|
||||||
"Box5": {"Box3": {"type": "section"}, "Box4": {"type": "section"}},
|
|
||||||
"Box6": {"Filter0": {"type": "filter", "filters": {"3": "!=Tax", "4": "Sales"}}},
|
|
||||||
"Box7": {"Filter0": {"type": "filter", "filters": {"2": "Expense", "3": "!=Tax"}}},
|
|
||||||
"Box8": {
|
|
||||||
"Filter0": {"type": "filter", "filters": {"3": "!=Tax", "4": "Sales", "12": "EU"}}
|
|
||||||
},
|
|
||||||
"Box9": {
|
|
||||||
"Filter0": {
|
|
||||||
"type": "filter",
|
|
||||||
"filters": {"2": "Expense", "3": "!=Tax", "12": "EU"},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"show_detail": 1,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
data = frappe.desk.query_report.run(
|
|
||||||
report_name,
|
|
||||||
filters={
|
|
||||||
"from_date": self.from_date,
|
|
||||||
"to_date": self.to_date,
|
|
||||||
"company": self.company.name,
|
|
||||||
"mode": "run",
|
|
||||||
"report_name": report_name,
|
|
||||||
},
|
|
||||||
user=frappe.session.user,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertListEqual(data.get("columns"), self.default_columns)
|
|
||||||
expected = (
|
|
||||||
("Box1", 43.25),
|
|
||||||
("Box2", 0.0),
|
|
||||||
("Box3", 43.25),
|
|
||||||
("Box4", -85.28),
|
|
||||||
("Box5", -42.03),
|
|
||||||
("Box6", 825.0),
|
|
||||||
("Box7", -426.40),
|
|
||||||
("Box8", 0.0),
|
|
||||||
("Box9", 0.0),
|
|
||||||
)
|
|
||||||
exrow = iter(expected)
|
|
||||||
for row in data.get("result"):
|
|
||||||
if row.get("voucher_no") and not row.get("posting_date"):
|
|
||||||
label, value = next(exrow)
|
|
||||||
self.assertDictEqual(row, {"voucher_no": label, "amount": value})
|
|
||||||
self.assertListEqual(
|
|
||||||
data.get("report_summary"),
|
|
||||||
[{"label": label, "datatype": "Currency", "value": value} for label, value in expected],
|
|
||||||
)
|
|
||||||
|
|
||||||
self.rm_testdocs()
|
|
||||||
|
|
||||||
def test_filter_match(self):
|
|
||||||
# None - treated as -inf number except range
|
|
||||||
self.assertTrue(filter_match(None, "!="))
|
|
||||||
self.assertTrue(filter_match(None, "<"))
|
|
||||||
self.assertTrue(filter_match(None, "<jjj"))
|
|
||||||
self.assertTrue(filter_match(None, " : "))
|
|
||||||
self.assertTrue(filter_match(None, ":56"))
|
|
||||||
self.assertTrue(filter_match(None, ":de"))
|
|
||||||
self.assertFalse(filter_match(None, "3.4"))
|
|
||||||
self.assertFalse(filter_match(None, "="))
|
|
||||||
self.assertFalse(filter_match(None, "=3.4"))
|
|
||||||
self.assertFalse(filter_match(None, ">3.4"))
|
|
||||||
self.assertFalse(filter_match(None, " <"))
|
|
||||||
self.assertFalse(filter_match(None, "ew"))
|
|
||||||
self.assertFalse(filter_match(None, " "))
|
|
||||||
self.assertFalse(filter_match(None, " f :"))
|
|
||||||
|
|
||||||
# Numbers
|
|
||||||
self.assertTrue(filter_match(3.4, "3.4"))
|
|
||||||
self.assertTrue(filter_match(3.4, ".4"))
|
|
||||||
self.assertTrue(filter_match(3.4, "3"))
|
|
||||||
self.assertTrue(filter_match(-3.4, "< -3"))
|
|
||||||
self.assertTrue(filter_match(-3.4, "> -4"))
|
|
||||||
self.assertTrue(filter_match(3.4, "= 3.4 "))
|
|
||||||
self.assertTrue(filter_match(3.4, "!=4.5"))
|
|
||||||
self.assertTrue(filter_match(3.4, " 3 : 4 "))
|
|
||||||
self.assertTrue(filter_match(0.0, " : "))
|
|
||||||
self.assertFalse(filter_match(3.4, "=4.5"))
|
|
||||||
self.assertFalse(filter_match(3.4, " = 3.4 "))
|
|
||||||
self.assertFalse(filter_match(3.4, "!=3.4"))
|
|
||||||
self.assertFalse(filter_match(3.4, ">6"))
|
|
||||||
self.assertFalse(filter_match(3.4, "<-4.5"))
|
|
||||||
self.assertFalse(filter_match(3.4, "4.5"))
|
|
||||||
self.assertFalse(filter_match(3.4, "5:9"))
|
|
||||||
|
|
||||||
# Strings
|
|
||||||
self.assertTrue(filter_match("ACC-SINV-2021-00001", "SINV"))
|
|
||||||
self.assertTrue(filter_match("ACC-SINV-2021-00001", "sinv"))
|
|
||||||
self.assertTrue(filter_match("ACC-SINV-2021-00001", "-2021"))
|
|
||||||
self.assertTrue(filter_match(" ACC-SINV-2021-00001", " acc"))
|
|
||||||
self.assertTrue(filter_match("ACC-SINV-2021-00001", "=2021"))
|
|
||||||
self.assertTrue(filter_match("ACC-SINV-2021-00001", "!=zz"))
|
|
||||||
self.assertTrue(filter_match("ACC-SINV-2021-00001", "< zzz "))
|
|
||||||
self.assertTrue(filter_match("ACC-SINV-2021-00001", " : sinv "))
|
|
||||||
self.assertFalse(filter_match("ACC-SINV-2021-00001", " sinv :"))
|
|
||||||
self.assertFalse(filter_match("ACC-SINV-2021-00001", " acc"))
|
|
||||||
self.assertFalse(filter_match("ACC-SINV-2021-00001", "= 2021 "))
|
|
||||||
self.assertFalse(filter_match("ACC-SINV-2021-00001", "!=sinv"))
|
|
||||||
self.assertFalse(filter_match("ACC-SINV-2021-00001", " >"))
|
|
||||||
self.assertFalse(filter_match("ACC-SINV-2021-00001", ">aa"))
|
|
||||||
self.assertFalse(filter_match("ACC-SINV-2021-00001", " <"))
|
|
||||||
self.assertFalse(filter_match("ACC-SINV-2021-00001", "< "))
|
|
||||||
self.assertFalse(filter_match("ACC-SINV-2021-00001", " ="))
|
|
||||||
self.assertFalse(filter_match("ACC-SINV-2021-00001", "="))
|
|
||||||
|
|
||||||
# Date - always match
|
|
||||||
self.assertTrue(filter_match(datetime.date(2021, 3, 19), " kdsjkldfs "))
|
|
||||||
@@ -29,10 +29,6 @@ REPORT_FILTER_TEST_CASES: list[tuple[ReportName, ReportFilters]] = [
|
|||||||
("Sales Register", {}),
|
("Sales Register", {}),
|
||||||
("Sales Register", {"item_group": "All Item Groups"}),
|
("Sales Register", {"item_group": "All Item Groups"}),
|
||||||
("Purchase Register", {}),
|
("Purchase Register", {}),
|
||||||
(
|
|
||||||
"Tax Detail",
|
|
||||||
{"mode": "run", "report_name": "Tax Detail"},
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
OPTIONAL_FILTERS = {}
|
OPTIONAL_FILTERS = {}
|
||||||
|
|||||||
@@ -415,9 +415,16 @@ class AccountsController(TransactionBase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def validate_invoice_documents_schedule(self):
|
def validate_invoice_documents_schedule(self):
|
||||||
if self.is_return:
|
if (
|
||||||
|
self.is_return
|
||||||
|
or (self.doctype == "Purchase Invoice" and self.is_paid)
|
||||||
|
or (self.doctype == "Sales Invoice" and self.is_pos)
|
||||||
|
or self.get("is_opening") == "Yes"
|
||||||
|
):
|
||||||
self.payment_terms_template = ""
|
self.payment_terms_template = ""
|
||||||
self.payment_schedule = []
|
self.payment_schedule = []
|
||||||
|
|
||||||
|
if self.is_return:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.validate_payment_schedule_dates()
|
self.validate_payment_schedule_dates()
|
||||||
@@ -2196,7 +2203,7 @@ class AccountsController(TransactionBase):
|
|||||||
frappe.throw(_("Rows with duplicate due dates in other rows were found: {0}").format(duplicates))
|
frappe.throw(_("Rows with duplicate due dates in other rows were found: {0}").format(duplicates))
|
||||||
|
|
||||||
def validate_payment_schedule_amount(self):
|
def validate_payment_schedule_amount(self):
|
||||||
if self.doctype == "Sales Invoice" and self.is_pos:
|
if (self.doctype == "Sales Invoice" and self.is_pos) or self.get("is_opening") == "Yes":
|
||||||
return
|
return
|
||||||
|
|
||||||
party_account_currency = self.get("party_account_currency")
|
party_account_currency = self.get("party_account_currency")
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ from erpnext.controllers.accounts_controller import (
|
|||||||
validate_inclusive_tax,
|
validate_inclusive_tax,
|
||||||
validate_taxes_and_charges,
|
validate_taxes_and_charges,
|
||||||
)
|
)
|
||||||
from erpnext.stock.get_item_details import _get_item_tax_template
|
from erpnext.stock.get_item_details import _get_item_tax_template, get_item_tax_map
|
||||||
from erpnext.utilities.regional import temporary_flag
|
from erpnext.utilities.regional import temporary_flag
|
||||||
|
|
||||||
|
|
||||||
@@ -67,6 +67,7 @@ class calculate_taxes_and_totals:
|
|||||||
self.validate_conversion_rate()
|
self.validate_conversion_rate()
|
||||||
self.calculate_item_values()
|
self.calculate_item_values()
|
||||||
self.validate_item_tax_template()
|
self.validate_item_tax_template()
|
||||||
|
self.update_item_tax_map()
|
||||||
self.initialize_taxes()
|
self.initialize_taxes()
|
||||||
self.determine_exclusive_rate()
|
self.determine_exclusive_rate()
|
||||||
self.calculate_net_total()
|
self.calculate_net_total()
|
||||||
@@ -130,6 +131,14 @@ class calculate_taxes_and_totals:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def update_item_tax_map(self):
|
||||||
|
for item in self.doc.items:
|
||||||
|
item.item_tax_rate = get_item_tax_map(
|
||||||
|
company=self.doc.get("company"),
|
||||||
|
item_tax_template=item.item_tax_template,
|
||||||
|
as_json=True,
|
||||||
|
)
|
||||||
|
|
||||||
def validate_conversion_rate(self):
|
def validate_conversion_rate(self):
|
||||||
# validate conversion rate
|
# validate conversion rate
|
||||||
company_currency = erpnext.get_company_currency(self.doc.company)
|
company_currency = erpnext.get_company_currency(self.doc.company)
|
||||||
|
|||||||
@@ -4,61 +4,67 @@ import frappe
|
|||||||
|
|
||||||
from erpnext.tests.utils import ReportFilters, ReportName, execute_script_report
|
from erpnext.tests.utils import ReportFilters, ReportName, execute_script_report
|
||||||
|
|
||||||
DEFAULT_FILTERS = {
|
|
||||||
"company": "_Test Company",
|
|
||||||
"from_date": "2010-01-01",
|
|
||||||
"to_date": "2030-01-01",
|
|
||||||
"warehouse": "_Test Warehouse - _TC",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
REPORT_FILTER_TEST_CASES: list[tuple[ReportName, ReportFilters]] = [
|
|
||||||
("BOM Explorer", {"bom": frappe.get_last_doc("BOM").name}),
|
|
||||||
("BOM Operations Time", {}),
|
|
||||||
("BOM Stock Calculated", {"bom": frappe.get_last_doc("BOM").name, "qty_to_make": 2}),
|
|
||||||
("BOM Stock Report", {"bom": frappe.get_last_doc("BOM").name, "qty_to_produce": 2}),
|
|
||||||
("Cost of Poor Quality Report", {"item": "_Test Item", "serial_no": "00"}),
|
|
||||||
("Downtime Analysis", {}),
|
|
||||||
(
|
|
||||||
"Exponential Smoothing Forecasting",
|
|
||||||
{
|
|
||||||
"based_on_document": "Sales Order",
|
|
||||||
"based_on_field": "Qty",
|
|
||||||
"no_of_years": 3,
|
|
||||||
"periodicity": "Yearly",
|
|
||||||
"smoothing_constant": 0.3,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
("Job Card Summary", {"fiscal_year": "2021-2022"}),
|
|
||||||
("Production Analytics", {"range": "Monthly"}),
|
|
||||||
("Quality Inspection Summary", {}),
|
|
||||||
("Process Loss Report", {}),
|
|
||||||
("Work Order Stock Report", {}),
|
|
||||||
("Work Order Summary", {"fiscal_year": "2021-2022", "age": 0}),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
if frappe.db.a_row_exists("Production Plan"):
|
|
||||||
REPORT_FILTER_TEST_CASES.append(
|
|
||||||
("Production Plan Summary", {"production_plan": frappe.get_last_doc("Production Plan").name})
|
|
||||||
)
|
|
||||||
|
|
||||||
OPTIONAL_FILTERS = {
|
|
||||||
"warehouse": "_Test Warehouse - _TC",
|
|
||||||
"item": "_Test Item",
|
|
||||||
"item_group": "_Test Item Group",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class TestManufacturingReports(unittest.TestCase):
|
class TestManufacturingReports(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.setup_default_filters()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
frappe.db.rollback()
|
||||||
|
|
||||||
|
def setup_default_filters(self):
|
||||||
|
self.last_bom = frappe.get_last_doc("BOM").name
|
||||||
|
self.DEFAULT_FILTERS = {
|
||||||
|
"company": "_Test Company",
|
||||||
|
"from_date": "2010-01-01",
|
||||||
|
"to_date": "2030-01-01",
|
||||||
|
"warehouse": "_Test Warehouse - _TC",
|
||||||
|
}
|
||||||
|
|
||||||
|
self.REPORT_FILTER_TEST_CASES: list[tuple[ReportName, ReportFilters]] = [
|
||||||
|
("BOM Explorer", {"bom": self.last_bom}),
|
||||||
|
("BOM Operations Time", {}),
|
||||||
|
("BOM Stock Calculated", {"bom": self.last_bom, "qty_to_make": 2}),
|
||||||
|
("BOM Stock Report", {"bom": self.last_bom, "qty_to_produce": 2}),
|
||||||
|
("Cost of Poor Quality Report", {"item": "_Test Item", "serial_no": "00"}),
|
||||||
|
("Downtime Analysis", {}),
|
||||||
|
(
|
||||||
|
"Exponential Smoothing Forecasting",
|
||||||
|
{
|
||||||
|
"based_on_document": "Sales Order",
|
||||||
|
"based_on_field": "Qty",
|
||||||
|
"no_of_years": 3,
|
||||||
|
"periodicity": "Yearly",
|
||||||
|
"smoothing_constant": 0.3,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
("Job Card Summary", {"fiscal_year": "2021-2022"}),
|
||||||
|
("Production Analytics", {"range": "Monthly"}),
|
||||||
|
("Quality Inspection Summary", {}),
|
||||||
|
("Process Loss Report", {}),
|
||||||
|
("Work Order Stock Report", {}),
|
||||||
|
("Work Order Summary", {"fiscal_year": "2021-2022", "age": 0}),
|
||||||
|
]
|
||||||
|
|
||||||
|
if frappe.db.a_row_exists("Production Plan"):
|
||||||
|
self.REPORT_FILTER_TEST_CASES.append(
|
||||||
|
("Production Plan Summary", {"production_plan": frappe.get_last_doc("Production Plan").name})
|
||||||
|
)
|
||||||
|
|
||||||
|
self.OPTIONAL_FILTERS = {
|
||||||
|
"warehouse": "_Test Warehouse - _TC",
|
||||||
|
"item": "_Test Item",
|
||||||
|
"item_group": "_Test Item Group",
|
||||||
|
}
|
||||||
|
|
||||||
def test_execute_all_manufacturing_reports(self):
|
def test_execute_all_manufacturing_reports(self):
|
||||||
"""Test that all script report in manufacturing modules are executable with supported filters"""
|
"""Test that all script report in manufacturing modules are executable with supported filters"""
|
||||||
for report, filter in REPORT_FILTER_TEST_CASES:
|
for report, filter in self.REPORT_FILTER_TEST_CASES:
|
||||||
with self.subTest(report=report):
|
with self.subTest(report=report):
|
||||||
execute_script_report(
|
execute_script_report(
|
||||||
report_name=report,
|
report_name=report,
|
||||||
module="Manufacturing",
|
module="Manufacturing",
|
||||||
filters=filter,
|
filters=filter,
|
||||||
default_filters=DEFAULT_FILTERS,
|
default_filters=self.DEFAULT_FILTERS,
|
||||||
optional_filters=OPTIONAL_FILTERS if filter.get("_optional") else None,
|
optional_filters=self.OPTIONAL_FILTERS if filter.get("_optional") else None,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -828,13 +828,13 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!this.frm.doc.is_return){
|
this.frm.doc.payments.find(payment => {
|
||||||
this.frm.doc.payments.find(payment => {
|
if (payment.default) {
|
||||||
if (payment.default) {
|
payment.amount = total_amount_to_pay;
|
||||||
payment.amount = total_amount_to_pay;
|
} else {
|
||||||
}
|
payment.amount = 0
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
this.frm.refresh_fields();
|
this.frm.refresh_fields();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -713,6 +713,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
}
|
}
|
||||||
|
|
||||||
validate() {
|
validate() {
|
||||||
|
this.apply_pricing_rule()
|
||||||
this.calculate_taxes_and_totals(false);
|
this.calculate_taxes_and_totals(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -841,6 +842,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
}
|
}
|
||||||
|
|
||||||
transaction_date() {
|
transaction_date() {
|
||||||
|
this.apply_pricing_rule()
|
||||||
if (this.frm.doc.transaction_date) {
|
if (this.frm.doc.transaction_date) {
|
||||||
this.frm.transaction_date = this.frm.doc.transaction_date;
|
this.frm.transaction_date = this.frm.doc.transaction_date;
|
||||||
frappe.ui.form.trigger(this.frm.doc.doctype, "currency");
|
frappe.ui.form.trigger(this.frm.doc.doctype, "currency");
|
||||||
@@ -849,6 +851,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
|
|
||||||
posting_date() {
|
posting_date() {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
me.apply_pricing_rule()
|
||||||
if (this.frm.doc.posting_date) {
|
if (this.frm.doc.posting_date) {
|
||||||
this.frm.posting_date = this.frm.doc.posting_date;
|
this.frm.posting_date = this.frm.doc.posting_date;
|
||||||
|
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ erpnext.PointOfSale.Controller = class {
|
|||||||
edit_cart: () => this.payment.edit_cart(),
|
edit_cart: () => this.payment.edit_cart(),
|
||||||
|
|
||||||
customer_details_updated: (details) => {
|
customer_details_updated: (details) => {
|
||||||
|
this.item_selector.load_items_data();
|
||||||
this.customer_details = details;
|
this.customer_details = details;
|
||||||
// will add/remove LP payment method
|
// will add/remove LP payment method
|
||||||
this.payment.render_loyalty_points_payment_mode();
|
this.payment.render_loyalty_points_payment_mode();
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ erpnext.PointOfSale.Payment = class {
|
|||||||
frappe.ui.form.on("Sales Invoice Payment", "amount", (frm, cdt, cdn) => {
|
frappe.ui.form.on("Sales Invoice Payment", "amount", (frm, cdt, cdn) => {
|
||||||
// for setting correct amount after loyalty points are redeemed
|
// for setting correct amount after loyalty points are redeemed
|
||||||
const default_mop = locals[cdt][cdn];
|
const default_mop = locals[cdt][cdn];
|
||||||
const mode = default_mop.mode_of_payment.replace(/ +/g, "_").toLowerCase();
|
const mode = this.sanitize_mode_of_payment(default_mop.mode_of_payment);
|
||||||
if (this[`${mode}_control`] && this[`${mode}_control`].get_value() != default_mop.amount) {
|
if (this[`${mode}_control`] && this[`${mode}_control`].get_value() != default_mop.amount) {
|
||||||
this[`${mode}_control`].set_value(default_mop.amount);
|
this[`${mode}_control`].set_value(default_mop.amount);
|
||||||
}
|
}
|
||||||
@@ -383,7 +383,7 @@ erpnext.PointOfSale.Payment = class {
|
|||||||
this.$payment_modes.html(
|
this.$payment_modes.html(
|
||||||
`${payments
|
`${payments
|
||||||
.map((p, i) => {
|
.map((p, i) => {
|
||||||
const mode = p.mode_of_payment.replace(/ +/g, "_").toLowerCase();
|
const mode = this.sanitize_mode_of_payment(p.mode_of_payment);
|
||||||
const payment_type = p.type;
|
const payment_type = p.type;
|
||||||
const margin = i % 2 === 0 ? "pr-2" : "pl-2";
|
const margin = i % 2 === 0 ? "pr-2" : "pl-2";
|
||||||
const amount = p.amount > 0 ? format_currency(p.amount, currency) : "";
|
const amount = p.amount > 0 ? format_currency(p.amount, currency) : "";
|
||||||
@@ -402,7 +402,7 @@ erpnext.PointOfSale.Payment = class {
|
|||||||
);
|
);
|
||||||
|
|
||||||
payments.forEach((p) => {
|
payments.forEach((p) => {
|
||||||
const mode = p.mode_of_payment.replace(/ +/g, "_").toLowerCase();
|
const mode = this.sanitize_mode_of_payment(p.mode_of_payment);
|
||||||
const me = this;
|
const me = this;
|
||||||
this[`${mode}_control`] = frappe.ui.form.make_control({
|
this[`${mode}_control`] = frappe.ui.form.make_control({
|
||||||
df: {
|
df: {
|
||||||
@@ -437,7 +437,7 @@ erpnext.PointOfSale.Payment = class {
|
|||||||
const doc = this.events.get_frm().doc;
|
const doc = this.events.get_frm().doc;
|
||||||
const payments = doc.payments;
|
const payments = doc.payments;
|
||||||
payments.forEach((p) => {
|
payments.forEach((p) => {
|
||||||
const mode = p.mode_of_payment.replace(/ +/g, "_").toLowerCase();
|
const mode = this.sanitize_mode_of_payment(p.mode_of_payment);
|
||||||
if (p.default) {
|
if (p.default) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$payment_modes.find(`.${mode}.mode-of-payment-control`).parent().click();
|
this.$payment_modes.find(`.${mode}.mode-of-payment-control`).parent().click();
|
||||||
@@ -607,4 +607,12 @@ erpnext.PointOfSale.Payment = class {
|
|||||||
toggle_component(show) {
|
toggle_component(show) {
|
||||||
show ? this.$component.css("display", "flex") : this.$component.css("display", "none");
|
show ? this.$component.css("display", "flex") : this.$component.css("display", "none");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sanitize_mode_of_payment(mode_of_payment) {
|
||||||
|
return mode_of_payment
|
||||||
|
.replace(/ +/g, "_")
|
||||||
|
.replace(/[^\p{L}\p{N}_-]/gu, "")
|
||||||
|
.replace(/^[^_a-zA-Z\p{L}]+/u, "")
|
||||||
|
.toLowerCase();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class ClosingStockBalance(Document):
|
|||||||
& (
|
& (
|
||||||
(table.from_date.between(self.from_date, self.to_date))
|
(table.from_date.between(self.from_date, self.to_date))
|
||||||
| (table.to_date.between(self.from_date, self.to_date))
|
| (table.to_date.between(self.from_date, self.to_date))
|
||||||
| ((table.from_date >= self.from_date) & (table.to_date >= self.to_date))
|
| ((self.from_date >= table.from_date) & (table.from_date >= self.to_date))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -640,7 +640,10 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False):
|
|||||||
if tax.valid_from or tax.maximum_net_rate:
|
if tax.valid_from or tax.maximum_net_rate:
|
||||||
# In purchase Invoice first preference will be given to supplier invoice date
|
# In purchase Invoice first preference will be given to supplier invoice date
|
||||||
# if supplier date is not present then posting date
|
# if supplier date is not present then posting date
|
||||||
validation_date = args.get("bill_date") or args.get("transaction_date")
|
|
||||||
|
validation_date = (
|
||||||
|
args.get("bill_date") or args.get("posting_date") or args.get("transaction_date")
|
||||||
|
)
|
||||||
|
|
||||||
if getdate(tax.valid_from) <= getdate(validation_date) and is_within_valid_range(args, tax):
|
if getdate(tax.valid_from) <= getdate(validation_date) and is_within_valid_range(args, tax):
|
||||||
taxes_with_validity.append(tax)
|
taxes_with_validity.append(tax)
|
||||||
|
|||||||
@@ -12,14 +12,14 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% for d in doc.taxes %}
|
{% for d in doc.taxes %}
|
||||||
{% if d.base_tax_amount %}
|
{% if d.tax_amount %}
|
||||||
<div class="order-taxes w-100 mt-5">
|
<div class="order-taxes w-100 mt-5">
|
||||||
<div class="col-4 d-flex border-btm pb-5">
|
<div class="col-4 d-flex border-btm pb-5">
|
||||||
<div class="item-grand-total col-8">
|
<div class="item-grand-total col-8">
|
||||||
{{ d.description }}
|
{{ d.description }}
|
||||||
</div>
|
</div>
|
||||||
<div class="item-grand-total col-4 text-right pr-0">
|
<div class="item-grand-total col-4 text-right pr-0">
|
||||||
{{ doc.get_formatted("net_total") }}
|
{{ d.get_formatted("tax_amount") }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user