Merge branch 'develop' into fix-depr-after-sale
This commit is contained in:
commit
28bdd6d2e3
@ -13,10 +13,12 @@
|
|||||||
"voucher_type",
|
"voucher_type",
|
||||||
"naming_series",
|
"naming_series",
|
||||||
"finance_book",
|
"finance_book",
|
||||||
|
"tax_withholding_category",
|
||||||
"column_break1",
|
"column_break1",
|
||||||
"from_template",
|
"from_template",
|
||||||
"company",
|
"company",
|
||||||
"posting_date",
|
"posting_date",
|
||||||
|
"apply_tds",
|
||||||
"2_add_edit_gl_entries",
|
"2_add_edit_gl_entries",
|
||||||
"accounts",
|
"accounts",
|
||||||
"section_break99",
|
"section_break99",
|
||||||
@ -498,16 +500,32 @@
|
|||||||
"options": "Journal Entry Template",
|
"options": "Journal Entry Template",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"report_hide": 1
|
"report_hide": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.apply_tds",
|
||||||
|
"fieldname": "tax_withholding_category",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Tax Withholding Category",
|
||||||
|
"mandatory_depends_on": "eval:doc.apply_tds",
|
||||||
|
"options": "Tax Withholding Category"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:['Credit Note', 'Debit Note'].includes(doc.voucher_type)",
|
||||||
|
"fieldname": "apply_tds",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Apply Tax Withholding Amount "
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 176,
|
"idx": 176,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-10-30 13:56:01.121995",
|
"modified": "2021-09-09 15:31:14.484029",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Journal Entry",
|
"name": "Journal Entry",
|
||||||
|
"naming_rule": "By \"Naming Series\" field",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
|
@ -15,6 +15,9 @@ from erpnext.accounts.deferred_revenue import get_deferred_booking_accounts
|
|||||||
from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import (
|
from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import (
|
||||||
get_party_account_based_on_invoice_discounting,
|
get_party_account_based_on_invoice_discounting,
|
||||||
)
|
)
|
||||||
|
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
|
||||||
|
get_party_tax_withholding_details,
|
||||||
|
)
|
||||||
from erpnext.accounts.party import get_party_account
|
from erpnext.accounts.party import get_party_account
|
||||||
from erpnext.accounts.utils import (
|
from erpnext.accounts.utils import (
|
||||||
check_if_stock_and_account_balance_synced,
|
check_if_stock_and_account_balance_synced,
|
||||||
@ -57,7 +60,8 @@ class JournalEntry(AccountsController):
|
|||||||
|
|
||||||
self.validate_against_jv()
|
self.validate_against_jv()
|
||||||
self.validate_reference_doc()
|
self.validate_reference_doc()
|
||||||
self.set_against_account()
|
if self.docstatus == 0:
|
||||||
|
self.set_against_account()
|
||||||
self.create_remarks()
|
self.create_remarks()
|
||||||
self.set_print_format_fields()
|
self.set_print_format_fields()
|
||||||
self.validate_expense_claim()
|
self.validate_expense_claim()
|
||||||
@ -66,6 +70,10 @@ class JournalEntry(AccountsController):
|
|||||||
self.set_account_and_party_balance()
|
self.set_account_and_party_balance()
|
||||||
self.validate_inter_company_accounts()
|
self.validate_inter_company_accounts()
|
||||||
self.validate_stock_accounts()
|
self.validate_stock_accounts()
|
||||||
|
|
||||||
|
if self.docstatus == 0:
|
||||||
|
self.apply_tax_withholding()
|
||||||
|
|
||||||
if not self.title:
|
if not self.title:
|
||||||
self.title = self.get_title()
|
self.title = self.get_title()
|
||||||
|
|
||||||
@ -139,6 +147,72 @@ class JournalEntry(AccountsController):
|
|||||||
frappe.throw(_("Account: {0} can only be updated via Stock Transactions")
|
frappe.throw(_("Account: {0} can only be updated via Stock Transactions")
|
||||||
.format(account), StockAccountInvalidTransaction)
|
.format(account), StockAccountInvalidTransaction)
|
||||||
|
|
||||||
|
def apply_tax_withholding(self):
|
||||||
|
from erpnext.accounts.report.general_ledger.general_ledger import get_account_type_map
|
||||||
|
|
||||||
|
if not self.apply_tds or self.voucher_type not in ('Debit Note', 'Credit Note'):
|
||||||
|
return
|
||||||
|
|
||||||
|
parties = [d.party for d in self.get('accounts') if d.party]
|
||||||
|
parties = list(set(parties))
|
||||||
|
|
||||||
|
if len(parties) > 1:
|
||||||
|
frappe.throw(_("Cannot apply TDS against multiple parties in one entry"))
|
||||||
|
|
||||||
|
account_type_map = get_account_type_map(self.company)
|
||||||
|
party_type = 'supplier' if self.voucher_type == 'Credit Note' else 'customer'
|
||||||
|
doctype = 'Purchase Invoice' if self.voucher_type == 'Credit Note' else 'Sales Invoice'
|
||||||
|
debit_or_credit = 'debit_in_account_currency' if self.voucher_type == 'Credit Note' else 'credit_in_account_currency'
|
||||||
|
rev_debit_or_credit = 'credit_in_account_currency' if debit_or_credit == 'debit_in_account_currency' else 'debit_in_account_currency'
|
||||||
|
|
||||||
|
party_account = get_party_account(party_type.title(), parties[0], self.company)
|
||||||
|
|
||||||
|
net_total = sum(d.get(debit_or_credit) for d in self.get('accounts') if account_type_map.get(d.account)
|
||||||
|
not in ('Tax', 'Chargeable'))
|
||||||
|
|
||||||
|
party_amount = sum(d.get(rev_debit_or_credit) for d in self.get('accounts') if d.account == party_account)
|
||||||
|
|
||||||
|
inv = frappe._dict({
|
||||||
|
party_type: parties[0],
|
||||||
|
'doctype': doctype,
|
||||||
|
'company': self.company,
|
||||||
|
'posting_date': self.posting_date,
|
||||||
|
'net_total': net_total
|
||||||
|
})
|
||||||
|
|
||||||
|
tax_withholding_details = get_party_tax_withholding_details(inv, self.tax_withholding_category)
|
||||||
|
|
||||||
|
if not tax_withholding_details:
|
||||||
|
return
|
||||||
|
|
||||||
|
accounts = []
|
||||||
|
for d in self.get('accounts'):
|
||||||
|
if d.get('account') == tax_withholding_details.get("account_head"):
|
||||||
|
d.update({
|
||||||
|
'account': tax_withholding_details.get("account_head"),
|
||||||
|
debit_or_credit: tax_withholding_details.get('tax_amount')
|
||||||
|
})
|
||||||
|
|
||||||
|
accounts.append(d.get('account'))
|
||||||
|
|
||||||
|
if d.get('account') == party_account:
|
||||||
|
d.update({
|
||||||
|
rev_debit_or_credit: party_amount - tax_withholding_details.get('tax_amount')
|
||||||
|
})
|
||||||
|
|
||||||
|
if not accounts or tax_withholding_details.get("account_head") not in accounts:
|
||||||
|
self.append("accounts", {
|
||||||
|
'account': tax_withholding_details.get("account_head"),
|
||||||
|
rev_debit_or_credit: tax_withholding_details.get('tax_amount'),
|
||||||
|
'against_account': parties[0]
|
||||||
|
})
|
||||||
|
|
||||||
|
to_remove = [d for d in self.get('accounts')
|
||||||
|
if not d.get(rev_debit_or_credit) and d.account == tax_withholding_details.get("account_head")]
|
||||||
|
|
||||||
|
for d in to_remove:
|
||||||
|
self.remove(d)
|
||||||
|
|
||||||
def update_inter_company_jv(self):
|
def update_inter_company_jv(self):
|
||||||
if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference:
|
if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference:
|
||||||
frappe.db.set_value("Journal Entry", self.inter_company_journal_entry_reference,\
|
frappe.db.set_value("Journal Entry", self.inter_company_journal_entry_reference,\
|
||||||
|
@ -93,6 +93,7 @@
|
|||||||
"options": "Payment Term"
|
"options": "Payment Term"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "exchange_gain_loss",
|
||||||
"fieldname": "exchange_gain_loss",
|
"fieldname": "exchange_gain_loss",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Exchange Gain/Loss",
|
"label": "Exchange Gain/Loss",
|
||||||
@ -103,7 +104,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-04-21 13:30:11.605388",
|
"modified": "2021-09-26 17:06:55.597389",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry Reference",
|
"name": "Payment Entry Reference",
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -15,6 +15,7 @@ from erpnext.accounts.deferred_revenue import validate_service_stop_date
|
|||||||
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
|
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
|
||||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
|
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
|
||||||
check_if_return_invoice_linked_with_payment_entry,
|
check_if_return_invoice_linked_with_payment_entry,
|
||||||
|
is_overdue,
|
||||||
unlink_inter_company_doc,
|
unlink_inter_company_doc,
|
||||||
update_linked_doc,
|
update_linked_doc,
|
||||||
validate_inter_company_party,
|
validate_inter_company_party,
|
||||||
@ -1145,6 +1146,12 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if not self.apply_tds:
|
if not self.apply_tds:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self.apply_tds and not self.get('tax_withholding_category'):
|
||||||
|
self.tax_withholding_category = frappe.db.get_value('Supplier', self.supplier, 'tax_withholding_category')
|
||||||
|
|
||||||
|
if not self.tax_withholding_category:
|
||||||
|
return
|
||||||
|
|
||||||
tax_withholding_details = get_party_tax_withholding_details(self, self.tax_withholding_category)
|
tax_withholding_details = get_party_tax_withholding_details(self, self.tax_withholding_category)
|
||||||
|
|
||||||
if not tax_withholding_details:
|
if not tax_withholding_details:
|
||||||
@ -1175,10 +1182,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.status = 'Draft'
|
self.status = 'Draft'
|
||||||
return
|
return
|
||||||
|
|
||||||
precision = self.precision("outstanding_amount")
|
outstanding_amount = flt(self.outstanding_amount, self.precision("outstanding_amount"))
|
||||||
outstanding_amount = flt(self.outstanding_amount, precision)
|
|
||||||
due_date = getdate(self.due_date)
|
|
||||||
nowdate = getdate()
|
|
||||||
|
|
||||||
if not status:
|
if not status:
|
||||||
if self.docstatus == 2:
|
if self.docstatus == 2:
|
||||||
@ -1186,9 +1190,11 @@ class PurchaseInvoice(BuyingController):
|
|||||||
elif self.docstatus == 1:
|
elif self.docstatus == 1:
|
||||||
if self.is_internal_transfer():
|
if self.is_internal_transfer():
|
||||||
self.status = 'Internal Transfer'
|
self.status = 'Internal Transfer'
|
||||||
elif outstanding_amount > 0 and due_date < nowdate:
|
elif is_overdue(self):
|
||||||
self.status = "Overdue"
|
self.status = "Overdue"
|
||||||
elif outstanding_amount > 0 and due_date >= nowdate:
|
elif 0 < outstanding_amount < flt(self.grand_total, self.precision("grand_total")):
|
||||||
|
self.status = "Partly Paid"
|
||||||
|
elif outstanding_amount > 0 and getdate(self.due_date) >= getdate():
|
||||||
self.status = "Unpaid"
|
self.status = "Unpaid"
|
||||||
#Check if outstanding amount is 0 due to debit note issued against invoice
|
#Check if outstanding amount is 0 due to debit note issued against invoice
|
||||||
elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
|
elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
|
||||||
|
@ -2,28 +2,58 @@
|
|||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
// render
|
// render
|
||||||
frappe.listview_settings['Purchase Invoice'] = {
|
frappe.listview_settings["Purchase Invoice"] = {
|
||||||
add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company",
|
add_fields: [
|
||||||
"currency", "is_return", "release_date", "on_hold", "represents_company", "is_internal_supplier"],
|
"supplier",
|
||||||
get_indicator: function(doc) {
|
"supplier_name",
|
||||||
if ((flt(doc.outstanding_amount) <= 0) && doc.docstatus == 1 && doc.status == 'Debit Note Issued') {
|
"base_grand_total",
|
||||||
return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<=,0"];
|
"outstanding_amount",
|
||||||
} else if (flt(doc.outstanding_amount) > 0 && doc.docstatus==1) {
|
"due_date",
|
||||||
if(cint(doc.on_hold) && !doc.release_date) {
|
"company",
|
||||||
return [__("On Hold"), "darkgrey"];
|
"currency",
|
||||||
} else if (cint(doc.on_hold) && doc.release_date && frappe.datetime.get_diff(doc.release_date, frappe.datetime.nowdate()) > 0) {
|
"is_return",
|
||||||
return [__("Temporarily on Hold"), "darkgrey"];
|
"release_date",
|
||||||
} else if (frappe.datetime.get_diff(doc.due_date) < 0) {
|
"on_hold",
|
||||||
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"];
|
"represents_company",
|
||||||
} else {
|
"is_internal_supplier",
|
||||||
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"];
|
],
|
||||||
}
|
get_indicator(doc) {
|
||||||
} else if (cint(doc.is_return)) {
|
if (doc.status == "Debit Note Issued") {
|
||||||
return [__("Return"), "gray", "is_return,=,Yes"];
|
return [__(doc.status), "darkgrey", "status,=," + doc.status];
|
||||||
} else if (doc.company == doc.represents_company && doc.is_internal_supplier) {
|
|
||||||
return [__("Internal Transfer"), "darkgrey", "outstanding_amount,=,0"];
|
|
||||||
} else if (flt(doc.outstanding_amount)==0 && doc.docstatus==1) {
|
|
||||||
return [__("Paid"), "green", "outstanding_amount,=,0"];
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (
|
||||||
|
flt(doc.outstanding_amount) > 0 &&
|
||||||
|
doc.docstatus == 1 &&
|
||||||
|
cint(doc.on_hold)
|
||||||
|
) {
|
||||||
|
if (!doc.release_date) {
|
||||||
|
return [__("On Hold"), "darkgrey"];
|
||||||
|
} else if (
|
||||||
|
frappe.datetime.get_diff(
|
||||||
|
doc.release_date,
|
||||||
|
frappe.datetime.nowdate()
|
||||||
|
) > 0
|
||||||
|
) {
|
||||||
|
return [__("Temporarily on Hold"), "darkgrey"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const status_colors = {
|
||||||
|
"Unpaid": "orange",
|
||||||
|
"Paid": "green",
|
||||||
|
"Return": "gray",
|
||||||
|
"Overdue": "red",
|
||||||
|
"Partly Paid": "yellow",
|
||||||
|
"Internal Transfer": "darkgrey",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (status_colors[doc.status]) {
|
||||||
|
return [
|
||||||
|
__(doc.status),
|
||||||
|
status_colors[doc.status],
|
||||||
|
"status,=," + doc.status,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -97,6 +97,7 @@
|
|||||||
"width": "100px"
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "exchange_gain_loss",
|
||||||
"fieldname": "exchange_gain_loss",
|
"fieldname": "exchange_gain_loss",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Exchange Gain/Loss",
|
"label": "Exchange Gain/Loss",
|
||||||
@ -104,6 +105,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "exchange_gain_loss",
|
||||||
"fieldname": "ref_exchange_rate",
|
"fieldname": "ref_exchange_rate",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Reference Exchange Rate",
|
"label": "Reference Exchange Rate",
|
||||||
@ -115,7 +117,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-04-20 16:26:53.820530",
|
"modified": "2021-09-26 15:47:28.167371",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice Advance",
|
"name": "Purchase Invoice Advance",
|
||||||
|
@ -1651,7 +1651,7 @@
|
|||||||
"label": "Status",
|
"label": "Status",
|
||||||
"length": 30,
|
"length": 30,
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nUnpaid and Discounted\nOverdue and Discounted\nOverdue\nCancelled\nInternal Transfer",
|
"options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nPartly Paid\nUnpaid\nUnpaid and Discounted\nPartly Paid and Discounted\nOverdue and Discounted\nOverdue\nCancelled\nInternal Transfer",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@ -2022,11 +2022,12 @@
|
|||||||
"link_fieldname": "consolidated_invoice"
|
"link_fieldname": "consolidated_invoice"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2021-09-08 15:24:25.486499",
|
"modified": "2021-09-21 09:27:50.191854",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
"name_case": "Title Case",
|
"name_case": "Title Case",
|
||||||
|
"naming_rule": "By \"Naming Series\" field",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
|
@ -1481,14 +1481,7 @@ class SalesInvoice(SellingController):
|
|||||||
self.status = 'Draft'
|
self.status = 'Draft'
|
||||||
return
|
return
|
||||||
|
|
||||||
precision = self.precision("outstanding_amount")
|
outstanding_amount = flt(self.outstanding_amount, self.precision("outstanding_amount"))
|
||||||
outstanding_amount = flt(self.outstanding_amount, precision)
|
|
||||||
due_date = getdate(self.due_date)
|
|
||||||
nowdate = getdate()
|
|
||||||
|
|
||||||
discounting_status = None
|
|
||||||
if self.is_discounted:
|
|
||||||
discounting_status = get_discounting_status(self.name)
|
|
||||||
|
|
||||||
if not status:
|
if not status:
|
||||||
if self.docstatus == 2:
|
if self.docstatus == 2:
|
||||||
@ -1496,15 +1489,13 @@ class SalesInvoice(SellingController):
|
|||||||
elif self.docstatus == 1:
|
elif self.docstatus == 1:
|
||||||
if self.is_internal_transfer():
|
if self.is_internal_transfer():
|
||||||
self.status = 'Internal Transfer'
|
self.status = 'Internal Transfer'
|
||||||
elif outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discounting_status=='Disbursed':
|
elif is_overdue(self):
|
||||||
self.status = "Overdue and Discounted"
|
|
||||||
elif outstanding_amount > 0 and due_date < nowdate:
|
|
||||||
self.status = "Overdue"
|
self.status = "Overdue"
|
||||||
elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discounting_status=='Disbursed':
|
elif 0 < outstanding_amount < flt(self.grand_total, self.precision("grand_total")):
|
||||||
self.status = "Unpaid and Discounted"
|
self.status = "Partly Paid"
|
||||||
elif outstanding_amount > 0 and due_date >= nowdate:
|
elif outstanding_amount > 0 and getdate(self.due_date) >= getdate():
|
||||||
self.status = "Unpaid"
|
self.status = "Unpaid"
|
||||||
#Check if outstanding amount is 0 due to credit note issued against invoice
|
# Check if outstanding amount is 0 due to credit note issued against invoice
|
||||||
elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
|
elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
|
||||||
self.status = "Credit Note Issued"
|
self.status = "Credit Note Issued"
|
||||||
elif self.is_return == 1:
|
elif self.is_return == 1:
|
||||||
@ -1513,12 +1504,42 @@ class SalesInvoice(SellingController):
|
|||||||
self.status = "Paid"
|
self.status = "Paid"
|
||||||
else:
|
else:
|
||||||
self.status = "Submitted"
|
self.status = "Submitted"
|
||||||
|
|
||||||
|
if (
|
||||||
|
self.status in ("Unpaid", "Partly Paid", "Overdue")
|
||||||
|
and self.is_discounted
|
||||||
|
and get_discounting_status(self.name) == "Disbursed"
|
||||||
|
):
|
||||||
|
self.status += " and Discounted"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.status = "Draft"
|
self.status = "Draft"
|
||||||
|
|
||||||
if update:
|
if update:
|
||||||
self.db_set('status', self.status, update_modified = update_modified)
|
self.db_set('status', self.status, update_modified = update_modified)
|
||||||
|
|
||||||
|
def is_overdue(doc):
|
||||||
|
outstanding_amount = flt(doc.outstanding_amount, doc.precision("outstanding_amount"))
|
||||||
|
|
||||||
|
if outstanding_amount <= 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
grand_total = flt(doc.grand_total, doc.precision("grand_total"))
|
||||||
|
nowdate = getdate()
|
||||||
|
if doc.payment_schedule:
|
||||||
|
# calculate payable amount till date
|
||||||
|
payable_amount = sum(
|
||||||
|
payment.payment_amount
|
||||||
|
for payment in doc.payment_schedule
|
||||||
|
if getdate(payment.due_date) < nowdate
|
||||||
|
)
|
||||||
|
|
||||||
|
if (grand_total - outstanding_amount) < payable_amount:
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif getdate(doc.due_date) < nowdate:
|
||||||
|
return True
|
||||||
|
|
||||||
def get_discounting_status(sales_invoice):
|
def get_discounting_status(sales_invoice):
|
||||||
status = None
|
status = None
|
||||||
|
|
||||||
|
@ -6,18 +6,20 @@ frappe.listview_settings['Sales Invoice'] = {
|
|||||||
add_fields: ["customer", "customer_name", "base_grand_total", "outstanding_amount", "due_date", "company",
|
add_fields: ["customer", "customer_name", "base_grand_total", "outstanding_amount", "due_date", "company",
|
||||||
"currency", "is_return"],
|
"currency", "is_return"],
|
||||||
get_indicator: function(doc) {
|
get_indicator: function(doc) {
|
||||||
var status_color = {
|
const status_colors = {
|
||||||
"Draft": "grey",
|
"Draft": "grey",
|
||||||
"Unpaid": "orange",
|
"Unpaid": "orange",
|
||||||
"Paid": "green",
|
"Paid": "green",
|
||||||
"Return": "gray",
|
"Return": "gray",
|
||||||
"Credit Note Issued": "gray",
|
"Credit Note Issued": "gray",
|
||||||
"Unpaid and Discounted": "orange",
|
"Unpaid and Discounted": "orange",
|
||||||
|
"Partly Paid and Discounted": "yellow",
|
||||||
"Overdue and Discounted": "red",
|
"Overdue and Discounted": "red",
|
||||||
"Overdue": "red",
|
"Overdue": "red",
|
||||||
|
"Partly Paid": "yellow",
|
||||||
"Internal Transfer": "darkgrey"
|
"Internal Transfer": "darkgrey"
|
||||||
};
|
};
|
||||||
return [__(doc.status), status_color[doc.status], "status,=,"+doc.status];
|
return [__(doc.status), status_colors[doc.status], "status,=,"+doc.status];
|
||||||
},
|
},
|
||||||
right_column: "grand_total"
|
right_column: "grand_total"
|
||||||
};
|
};
|
||||||
|
@ -133,6 +133,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
def test_payment_entry_unlink_against_invoice(self):
|
def test_payment_entry_unlink_against_invoice(self):
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
||||||
|
|
||||||
si = frappe.copy_doc(test_records[0])
|
si = frappe.copy_doc(test_records[0])
|
||||||
si.is_pos = 0
|
si.is_pos = 0
|
||||||
si.insert()
|
si.insert()
|
||||||
@ -156,6 +157,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
def test_payment_entry_unlink_against_standalone_credit_note(self):
|
def test_payment_entry_unlink_against_standalone_credit_note(self):
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
||||||
|
|
||||||
si1 = create_sales_invoice(rate=1000)
|
si1 = create_sales_invoice(rate=1000)
|
||||||
si2 = create_sales_invoice(rate=300)
|
si2 = create_sales_invoice(rate=300)
|
||||||
si3 = create_sales_invoice(qty=-1, rate=300, is_return=1)
|
si3 = create_sales_invoice(qty=-1, rate=300, is_return=1)
|
||||||
@ -1646,6 +1648,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
def test_credit_note(self):
|
def test_credit_note(self):
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
||||||
|
|
||||||
si = create_sales_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1)
|
si = create_sales_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1)
|
||||||
|
|
||||||
outstanding_amount = get_outstanding_amount(si.doctype,
|
outstanding_amount = get_outstanding_amount(si.doctype,
|
||||||
@ -2295,6 +2298,54 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
party_link.delete()
|
party_link.delete()
|
||||||
frappe.db.set_value('Accounts Settings', None, 'enable_common_party_accounting', 0)
|
frappe.db.set_value('Accounts Settings', None, 'enable_common_party_accounting', 0)
|
||||||
|
|
||||||
|
def test_payment_statuses(self):
|
||||||
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
||||||
|
|
||||||
|
today = nowdate()
|
||||||
|
|
||||||
|
# Test Overdue
|
||||||
|
si = create_sales_invoice(do_not_submit=True)
|
||||||
|
si.payment_schedule = []
|
||||||
|
si.append("payment_schedule", {
|
||||||
|
"due_date": add_days(today, -5),
|
||||||
|
"invoice_portion": 50,
|
||||||
|
"payment_amount": si.grand_total / 2
|
||||||
|
})
|
||||||
|
si.append("payment_schedule", {
|
||||||
|
"due_date": add_days(today, 5),
|
||||||
|
"invoice_portion": 50,
|
||||||
|
"payment_amount": si.grand_total / 2
|
||||||
|
})
|
||||||
|
si.submit()
|
||||||
|
self.assertEqual(si.status, "Overdue")
|
||||||
|
|
||||||
|
# Test payment less than due amount
|
||||||
|
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
|
||||||
|
pe.reference_no = "1"
|
||||||
|
pe.reference_date = nowdate()
|
||||||
|
pe.paid_amount = 1
|
||||||
|
pe.references[0].allocated_amount = pe.paid_amount
|
||||||
|
pe.submit()
|
||||||
|
si.reload()
|
||||||
|
self.assertEqual(si.status, "Overdue")
|
||||||
|
|
||||||
|
# Test Partly Paid
|
||||||
|
pe = frappe.copy_doc(pe)
|
||||||
|
pe.paid_amount = si.grand_total / 2
|
||||||
|
pe.references[0].allocated_amount = pe.paid_amount
|
||||||
|
pe.submit()
|
||||||
|
si.reload()
|
||||||
|
self.assertEqual(si.status, "Partly Paid")
|
||||||
|
|
||||||
|
# Test Paid
|
||||||
|
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
|
||||||
|
pe.reference_no = "1"
|
||||||
|
pe.reference_date = nowdate()
|
||||||
|
pe.paid_amount = si.outstanding_amount
|
||||||
|
pe.submit()
|
||||||
|
si.reload()
|
||||||
|
self.assertEqual(si.status, "Paid")
|
||||||
|
|
||||||
def get_sales_invoice_for_e_invoice():
|
def get_sales_invoice_for_e_invoice():
|
||||||
si = make_sales_invoice_for_ewaybill()
|
si = make_sales_invoice_for_ewaybill()
|
||||||
si.naming_series = 'INV-2020-.#####'
|
si.naming_series = 'INV-2020-.#####'
|
||||||
@ -2327,6 +2378,7 @@ def make_test_address_for_ewaybill():
|
|||||||
if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
|
if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
|
||||||
address = frappe.get_doc({
|
address = frappe.get_doc({
|
||||||
"address_line1": "_Test Address Line 1",
|
"address_line1": "_Test Address Line 1",
|
||||||
|
"address_line2": "_Test Address Line 2",
|
||||||
"address_title": "_Test Address for Eway bill",
|
"address_title": "_Test Address for Eway bill",
|
||||||
"address_type": "Billing",
|
"address_type": "Billing",
|
||||||
"city": "_Test City",
|
"city": "_Test City",
|
||||||
@ -2348,11 +2400,12 @@ def make_test_address_for_ewaybill():
|
|||||||
|
|
||||||
address.save()
|
address.save()
|
||||||
|
|
||||||
if not frappe.db.exists('Address', '_Test Customer-Address for Eway bill-Shipping'):
|
if not frappe.db.exists('Address', '_Test Customer-Address for Eway bill-Billing'):
|
||||||
address = frappe.get_doc({
|
address = frappe.get_doc({
|
||||||
"address_line1": "_Test Address Line 1",
|
"address_line1": "_Test Address Line 1",
|
||||||
|
"address_line2": "_Test Address Line 2",
|
||||||
"address_title": "_Test Customer-Address for Eway bill",
|
"address_title": "_Test Customer-Address for Eway bill",
|
||||||
"address_type": "Shipping",
|
"address_type": "Billing",
|
||||||
"city": "_Test City",
|
"city": "_Test City",
|
||||||
"state": "Test State",
|
"state": "Test State",
|
||||||
"country": "India",
|
"country": "India",
|
||||||
@ -2372,9 +2425,34 @@ def make_test_address_for_ewaybill():
|
|||||||
|
|
||||||
address.save()
|
address.save()
|
||||||
|
|
||||||
|
if not frappe.db.exists('Address', '_Test Customer-Address for Eway bill-Shipping'):
|
||||||
|
address = frappe.get_doc({
|
||||||
|
"address_line1": "_Test Address Line 1",
|
||||||
|
"address_line2": "_Test Address Line 2",
|
||||||
|
"address_title": "_Test Customer-Address for Eway bill",
|
||||||
|
"address_type": "Shipping",
|
||||||
|
"city": "_Test City",
|
||||||
|
"state": "Test State",
|
||||||
|
"country": "India",
|
||||||
|
"doctype": "Address",
|
||||||
|
"is_primary_address": 1,
|
||||||
|
"phone": "+910000000000",
|
||||||
|
"gst_state": "Maharashtra",
|
||||||
|
"gst_state_number": "27",
|
||||||
|
"pincode": "410098"
|
||||||
|
}).insert()
|
||||||
|
|
||||||
|
address.append("links", {
|
||||||
|
"link_doctype": "Customer",
|
||||||
|
"link_name": "_Test Customer"
|
||||||
|
})
|
||||||
|
|
||||||
|
address.save()
|
||||||
|
|
||||||
if not frappe.db.exists('Address', '_Test Dispatch-Address for Eway bill-Shipping'):
|
if not frappe.db.exists('Address', '_Test Dispatch-Address for Eway bill-Shipping'):
|
||||||
address = frappe.get_doc({
|
address = frappe.get_doc({
|
||||||
"address_line1": "_Test Dispatch Address Line 1",
|
"address_line1": "_Test Dispatch Address Line 1",
|
||||||
|
"address_line2": "_Test Dispatch Address Line 2",
|
||||||
"address_title": "_Test Dispatch-Address for Eway bill",
|
"address_title": "_Test Dispatch-Address for Eway bill",
|
||||||
"address_type": "Shipping",
|
"address_type": "Shipping",
|
||||||
"city": "_Test City",
|
"city": "_Test City",
|
||||||
@ -2389,11 +2467,6 @@ def make_test_address_for_ewaybill():
|
|||||||
"pincode": "1100101"
|
"pincode": "1100101"
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
address.append("links", {
|
|
||||||
"link_doctype": "Company",
|
|
||||||
"link_name": "_Test Company"
|
|
||||||
})
|
|
||||||
|
|
||||||
address.save()
|
address.save()
|
||||||
|
|
||||||
def make_test_transporter_for_ewaybill():
|
def make_test_transporter_for_ewaybill():
|
||||||
@ -2433,7 +2506,8 @@ def make_sales_invoice_for_ewaybill():
|
|||||||
|
|
||||||
si.distance = 2000
|
si.distance = 2000
|
||||||
si.company_address = "_Test Address for Eway bill-Billing"
|
si.company_address = "_Test Address for Eway bill-Billing"
|
||||||
si.customer_address = "_Test Customer-Address for Eway bill-Shipping"
|
si.customer_address = "_Test Customer-Address for Eway bill-Billing"
|
||||||
|
si.shipping_address_name = "_Test Customer-Address for Eway bill-Shipping"
|
||||||
si.dispatch_address_name = "_Test Dispatch-Address for Eway bill-Shipping"
|
si.dispatch_address_name = "_Test Dispatch-Address for Eway bill-Shipping"
|
||||||
si.vehicle_no = "KA12KA1234"
|
si.vehicle_no = "KA12KA1234"
|
||||||
si.gst_category = "Registered Regular"
|
si.gst_category = "Registered Regular"
|
||||||
|
@ -98,6 +98,7 @@
|
|||||||
"width": "120px"
|
"width": "120px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "exchange_gain_loss",
|
||||||
"fieldname": "exchange_gain_loss",
|
"fieldname": "exchange_gain_loss",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Exchange Gain/Loss",
|
"label": "Exchange Gain/Loss",
|
||||||
@ -105,6 +106,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "exchange_gain_loss",
|
||||||
"fieldname": "ref_exchange_rate",
|
"fieldname": "ref_exchange_rate",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Reference Exchange Rate",
|
"label": "Reference Exchange Rate",
|
||||||
@ -116,7 +118,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-06-04 20:25:49.832052",
|
"modified": "2021-09-26 15:47:46.911595",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice Advance",
|
"name": "Sales Invoice Advance",
|
||||||
|
@ -100,6 +100,7 @@ def get_tax_withholding_details(tax_withholding_category, posting_date, company)
|
|||||||
for account_detail in tax_withholding.accounts:
|
for account_detail in tax_withholding.accounts:
|
||||||
if company == account_detail.company:
|
if company == account_detail.company:
|
||||||
return frappe._dict({
|
return frappe._dict({
|
||||||
|
"tax_withholding_category": tax_withholding_category,
|
||||||
"account_head": account_detail.account,
|
"account_head": account_detail.account,
|
||||||
"rate": tax_rate_detail.tax_withholding_rate,
|
"rate": tax_rate_detail.tax_withholding_rate,
|
||||||
"from_date": tax_rate_detail.from_date,
|
"from_date": tax_rate_detail.from_date,
|
||||||
@ -206,18 +207,39 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
|
|||||||
|
|
||||||
def get_invoice_vouchers(parties, tax_details, company, party_type='Supplier'):
|
def get_invoice_vouchers(parties, tax_details, company, party_type='Supplier'):
|
||||||
dr_or_cr = 'credit' if party_type == 'Supplier' else 'debit'
|
dr_or_cr = 'credit' if party_type == 'Supplier' else 'debit'
|
||||||
|
doctype = 'Purchase Invoice' if party_type == 'Supplier' else 'Sales Invoice'
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
dr_or_cr: ['>', 0],
|
|
||||||
'company': company,
|
'company': company,
|
||||||
'party_type': party_type,
|
frappe.scrub(party_type): ['in', parties],
|
||||||
'party': ['in', parties],
|
|
||||||
'posting_date': ['between', (tax_details.from_date, tax_details.to_date)],
|
'posting_date': ['between', (tax_details.from_date, tax_details.to_date)],
|
||||||
'is_opening': 'No',
|
'is_opening': 'No',
|
||||||
'is_cancelled': 0
|
'docstatus': 1
|
||||||
}
|
}
|
||||||
|
|
||||||
return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck="voucher_no") or [""]
|
if not tax_details.get('consider_party_ledger_amount') and doctype != "Sales Invoice":
|
||||||
|
filters.update({
|
||||||
|
'apply_tds': 1,
|
||||||
|
'tax_withholding_category': tax_details.get('tax_withholding_category')
|
||||||
|
})
|
||||||
|
|
||||||
|
invoices = frappe.get_all(doctype, filters=filters, pluck="name") or [""]
|
||||||
|
|
||||||
|
journal_entries = frappe.db.sql("""
|
||||||
|
SELECT j.name
|
||||||
|
FROM `tabJournal Entry` j, `tabJournal Entry Account` ja
|
||||||
|
WHERE
|
||||||
|
j.docstatus = 1
|
||||||
|
AND j.is_opening = 'No'
|
||||||
|
AND j.posting_date between %s and %s
|
||||||
|
AND ja.{dr_or_cr} > 0
|
||||||
|
AND ja.party in %s
|
||||||
|
""".format(dr_or_cr=dr_or_cr), (tax_details.from_date, tax_details.to_date, tuple(parties)), as_list=1)
|
||||||
|
|
||||||
|
if journal_entries:
|
||||||
|
journal_entries = journal_entries[0]
|
||||||
|
|
||||||
|
return invoices + journal_entries
|
||||||
|
|
||||||
def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, party_type='Supplier'):
|
def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, party_type='Supplier'):
|
||||||
# for advance vouchers, debit and credit is reversed
|
# for advance vouchers, debit and credit is reversed
|
||||||
|
@ -176,6 +176,29 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
|||||||
for d in invoices:
|
for d in invoices:
|
||||||
d.cancel()
|
d.cancel()
|
||||||
|
|
||||||
|
def test_multi_category_single_supplier(self):
|
||||||
|
frappe.db.set_value("Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category")
|
||||||
|
invoices = []
|
||||||
|
|
||||||
|
pi = create_purchase_invoice(supplier = "Test TDS Supplier5", rate = 500, do_not_save=True)
|
||||||
|
pi.tax_withholding_category = "Test Service Category"
|
||||||
|
pi.save()
|
||||||
|
pi.submit()
|
||||||
|
invoices.append(pi)
|
||||||
|
|
||||||
|
# Second Invoice will apply TDS checked
|
||||||
|
pi1 = create_purchase_invoice(supplier = "Test TDS Supplier5", rate = 2500, do_not_save=True)
|
||||||
|
pi1.tax_withholding_category = "Test Goods Category"
|
||||||
|
pi1.save()
|
||||||
|
pi1.submit()
|
||||||
|
invoices.append(pi1)
|
||||||
|
|
||||||
|
self.assertEqual(pi1.taxes[0].tax_amount, 250)
|
||||||
|
|
||||||
|
#delete invoices to avoid clashing
|
||||||
|
for d in invoices:
|
||||||
|
d.cancel()
|
||||||
|
|
||||||
def cancel_invoices():
|
def cancel_invoices():
|
||||||
purchase_invoices = frappe.get_all("Purchase Invoice", {
|
purchase_invoices = frappe.get_all("Purchase Invoice", {
|
||||||
'supplier': ['in', ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']],
|
'supplier': ['in', ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']],
|
||||||
@ -251,7 +274,8 @@ def create_sales_invoice(**args):
|
|||||||
|
|
||||||
def create_records():
|
def create_records():
|
||||||
# create a new suppliers
|
# create a new suppliers
|
||||||
for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2', 'Test TDS Supplier3', 'Test TDS Supplier4']:
|
for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2', 'Test TDS Supplier3',
|
||||||
|
'Test TDS Supplier4', 'Test TDS Supplier5']:
|
||||||
if frappe.db.exists('Supplier', name):
|
if frappe.db.exists('Supplier', name):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -390,3 +414,39 @@ def create_tax_with_holding_category():
|
|||||||
'account': 'TDS - _TC'
|
'account': 'TDS - _TC'
|
||||||
}]
|
}]
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
|
if not frappe.db.exists("Tax Withholding Category", "Test Service Category"):
|
||||||
|
frappe.get_doc({
|
||||||
|
"doctype": "Tax Withholding Category",
|
||||||
|
"name": "Test Service Category",
|
||||||
|
"category_name": "Test Service Category",
|
||||||
|
"rates": [{
|
||||||
|
'from_date': fiscal_year[1],
|
||||||
|
'to_date': fiscal_year[2],
|
||||||
|
'tax_withholding_rate': 10,
|
||||||
|
'single_threshold': 2000,
|
||||||
|
'cumulative_threshold': 2000
|
||||||
|
}],
|
||||||
|
"accounts": [{
|
||||||
|
'company': '_Test Company',
|
||||||
|
'account': 'TDS - _TC'
|
||||||
|
}]
|
||||||
|
}).insert()
|
||||||
|
|
||||||
|
if not frappe.db.exists("Tax Withholding Category", "Test Goods Category"):
|
||||||
|
frappe.get_doc({
|
||||||
|
"doctype": "Tax Withholding Category",
|
||||||
|
"name": "Test Goods Category",
|
||||||
|
"category_name": "Test Goods Category",
|
||||||
|
"rates": [{
|
||||||
|
'from_date': fiscal_year[1],
|
||||||
|
'to_date': fiscal_year[2],
|
||||||
|
'tax_withholding_rate': 10,
|
||||||
|
'single_threshold': 2000,
|
||||||
|
'cumulative_threshold': 2000
|
||||||
|
}],
|
||||||
|
"accounts": [{
|
||||||
|
'company': '_Test Company',
|
||||||
|
'account': 'TDS - _TC'
|
||||||
|
}]
|
||||||
|
}).insert()
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
{
|
{
|
||||||
"add_total_row": 0,
|
"add_total_row": 1,
|
||||||
|
"columns": [],
|
||||||
"creation": "2018-08-21 11:25:00.551823",
|
"creation": "2018-08-21 11:25:00.551823",
|
||||||
|
"disable_prepared_report": 0,
|
||||||
"disabled": 0,
|
"disabled": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Report",
|
"doctype": "Report",
|
||||||
|
"filters": [],
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": "Yes",
|
"is_standard": "Yes",
|
||||||
"modified": "2018-09-21 11:25:00.551823",
|
"modified": "2021-09-20 17:43:39.518851",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "TDS Computation Summary",
|
"name": "TDS Computation Summary",
|
||||||
|
@ -2,11 +2,10 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import flt
|
|
||||||
|
|
||||||
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
|
from erpnext.accounts.report.tds_payable_monthly.tds_payable_monthly import (
|
||||||
get_advance_vouchers,
|
get_result,
|
||||||
get_debit_note_amount,
|
get_tds_docs,
|
||||||
)
|
)
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
|
|
||||||
@ -17,9 +16,12 @@ def execute(filters=None):
|
|||||||
filters.naming_series = frappe.db.get_single_value('Buying Settings', 'supp_master_name')
|
filters.naming_series = frappe.db.get_single_value('Buying Settings', 'supp_master_name')
|
||||||
|
|
||||||
columns = get_columns(filters)
|
columns = get_columns(filters)
|
||||||
res = get_result(filters)
|
tds_docs, tds_accounts, tax_category_map = get_tds_docs(filters)
|
||||||
|
|
||||||
return columns, res
|
res = get_result(filters, tds_docs, tds_accounts, tax_category_map)
|
||||||
|
final_result = group_by_supplier_and_category(res)
|
||||||
|
|
||||||
|
return columns, final_result
|
||||||
|
|
||||||
def validate_filters(filters):
|
def validate_filters(filters):
|
||||||
''' Validate if dates are properly set and lie in the same fiscal year'''
|
''' Validate if dates are properly set and lie in the same fiscal year'''
|
||||||
@ -33,81 +35,39 @@ def validate_filters(filters):
|
|||||||
|
|
||||||
filters["fiscal_year"] = from_year
|
filters["fiscal_year"] = from_year
|
||||||
|
|
||||||
def get_result(filters):
|
def group_by_supplier_and_category(data):
|
||||||
# if no supplier selected, fetch data for all tds applicable supplier
|
supplier_category_wise_map = {}
|
||||||
# else fetch relevant data for selected supplier
|
|
||||||
pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id"
|
|
||||||
fields = ["name", pan+" as pan", "tax_withholding_category", "supplier_type", "supplier_name"]
|
|
||||||
|
|
||||||
if filters.supplier:
|
for row in data:
|
||||||
filters.supplier = frappe.db.get_list('Supplier',
|
supplier_category_wise_map.setdefault((row.get('supplier'), row.get('section_code')), {
|
||||||
{"name": filters.supplier}, fields)
|
'pan': row.get('pan'),
|
||||||
else:
|
'supplier': row.get('supplier'),
|
||||||
filters.supplier = frappe.db.get_list('Supplier',
|
'supplier_name': row.get('supplier_name'),
|
||||||
{"tax_withholding_category": ["!=", ""]}, fields)
|
'section_code': row.get('section_code'),
|
||||||
|
'entity_type': row.get('entity_type'),
|
||||||
|
'tds_rate': row.get('tds_rate'),
|
||||||
|
'total_amount_credited': 0.0,
|
||||||
|
'tds_deducted': 0.0
|
||||||
|
})
|
||||||
|
|
||||||
|
supplier_category_wise_map.get((row.get('supplier'), row.get('section_code')))['total_amount_credited'] += \
|
||||||
|
row.get('total_amount_credited', 0.0)
|
||||||
|
|
||||||
|
supplier_category_wise_map.get((row.get('supplier'), row.get('section_code')))['tds_deducted'] += \
|
||||||
|
row.get('tds_deducted', 0.0)
|
||||||
|
|
||||||
|
final_result = get_final_result(supplier_category_wise_map)
|
||||||
|
|
||||||
|
return final_result
|
||||||
|
|
||||||
|
|
||||||
|
def get_final_result(supplier_category_wise_map):
|
||||||
out = []
|
out = []
|
||||||
for supplier in filters.supplier:
|
for key, value in supplier_category_wise_map.items():
|
||||||
tds = frappe.get_doc("Tax Withholding Category", supplier.tax_withholding_category)
|
out.append(value)
|
||||||
rate = [d.tax_withholding_rate for d in tds.rates if d.fiscal_year == filters.fiscal_year]
|
|
||||||
|
|
||||||
if rate:
|
|
||||||
rate = rate[0]
|
|
||||||
|
|
||||||
try:
|
|
||||||
account = [d.account for d in tds.accounts if d.company == filters.company][0]
|
|
||||||
|
|
||||||
except IndexError:
|
|
||||||
account = []
|
|
||||||
total_invoiced_amount, tds_deducted = get_invoice_and_tds_amount(supplier.name, account,
|
|
||||||
filters.company, filters.from_date, filters.to_date, filters.fiscal_year)
|
|
||||||
|
|
||||||
if total_invoiced_amount or tds_deducted:
|
|
||||||
row = [supplier.pan, supplier.name]
|
|
||||||
|
|
||||||
if filters.naming_series == 'Naming Series':
|
|
||||||
row.append(supplier.supplier_name)
|
|
||||||
|
|
||||||
row.extend([tds.name, supplier.supplier_type, rate, total_invoiced_amount, tds_deducted])
|
|
||||||
out.append(row)
|
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def get_invoice_and_tds_amount(supplier, account, company, from_date, to_date, fiscal_year):
|
|
||||||
''' calculate total invoice amount and total tds deducted for given supplier '''
|
|
||||||
|
|
||||||
entries = frappe.db.sql("""
|
|
||||||
select voucher_no, credit
|
|
||||||
from `tabGL Entry`
|
|
||||||
where party in (%s) and credit > 0
|
|
||||||
and company=%s and is_cancelled = 0
|
|
||||||
and posting_date between %s and %s
|
|
||||||
""", (supplier, company, from_date, to_date), as_dict=1)
|
|
||||||
|
|
||||||
supplier_credit_amount = flt(sum(d.credit for d in entries))
|
|
||||||
|
|
||||||
vouchers = [d.voucher_no for d in entries]
|
|
||||||
vouchers += get_advance_vouchers([supplier], company=company,
|
|
||||||
from_date=from_date, to_date=to_date)
|
|
||||||
|
|
||||||
tds_deducted = 0
|
|
||||||
if vouchers:
|
|
||||||
tds_deducted = flt(frappe.db.sql("""
|
|
||||||
select sum(credit)
|
|
||||||
from `tabGL Entry`
|
|
||||||
where account=%s and posting_date between %s and %s
|
|
||||||
and company=%s and credit > 0 and voucher_no in ({0})
|
|
||||||
""".format(', '.join("'%s'" % d for d in vouchers)),
|
|
||||||
(account, from_date, to_date, company))[0][0])
|
|
||||||
|
|
||||||
date_range_filter = [fiscal_year, from_date, to_date]
|
|
||||||
|
|
||||||
debit_note_amount = get_debit_note_amount([supplier], date_range_filter, company=company)
|
|
||||||
|
|
||||||
total_invoiced_amount = supplier_credit_amount + tds_deducted - debit_note_amount
|
|
||||||
|
|
||||||
return total_invoiced_amount, tds_deducted
|
|
||||||
|
|
||||||
def get_columns(filters):
|
def get_columns(filters):
|
||||||
columns = [
|
columns = [
|
||||||
{
|
{
|
||||||
@ -149,7 +109,7 @@ def get_columns(filters):
|
|||||||
{
|
{
|
||||||
"label": _("TDS Rate %"),
|
"label": _("TDS Rate %"),
|
||||||
"fieldname": "tds_rate",
|
"fieldname": "tds_rate",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Percent",
|
||||||
"width": 90
|
"width": 90
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -16,69 +16,6 @@ frappe.query_reports["TDS Payable Monthly"] = {
|
|||||||
"label": __("Supplier"),
|
"label": __("Supplier"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Supplier",
|
"options": "Supplier",
|
||||||
"get_query": function() {
|
|
||||||
return {
|
|
||||||
"filters": {
|
|
||||||
"tax_withholding_category": ["!=", ""],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
on_change: function() {
|
|
||||||
frappe.query_report.set_filter_value("purchase_invoice", "");
|
|
||||||
frappe.query_report.refresh();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname":"purchase_invoice",
|
|
||||||
"label": __("Purchase Invoice"),
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"options": "Purchase Invoice",
|
|
||||||
"get_query": function() {
|
|
||||||
return {
|
|
||||||
"filters": {
|
|
||||||
"name": ["in", frappe.query_report.invoices]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
on_change: function() {
|
|
||||||
let supplier = frappe.query_report.get_filter_value('supplier');
|
|
||||||
if(!supplier) return; // return if no supplier selected
|
|
||||||
|
|
||||||
// filter invoices based on selected supplier
|
|
||||||
let invoices = [];
|
|
||||||
frappe.query_report.invoice_data.map(d => {
|
|
||||||
if(d.supplier==supplier)
|
|
||||||
invoices.push(d.name)
|
|
||||||
});
|
|
||||||
frappe.query_report.invoices = invoices;
|
|
||||||
frappe.query_report.refresh();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname":"purchase_order",
|
|
||||||
"label": __("Purchase Order"),
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"options": "Purchase Order",
|
|
||||||
"get_query": function() {
|
|
||||||
return {
|
|
||||||
"filters": {
|
|
||||||
"name": ["in", frappe.query_report.invoices]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
on_change: function() {
|
|
||||||
let supplier = frappe.query_report.get_filter_value('supplier');
|
|
||||||
if(!supplier) return; // return if no supplier selected
|
|
||||||
|
|
||||||
// filter invoices based on selected supplier
|
|
||||||
let invoices = [];
|
|
||||||
frappe.query_report.invoice_data.map(d => {
|
|
||||||
if(d.supplier==supplier)
|
|
||||||
invoices.push(d.name)
|
|
||||||
});
|
|
||||||
frappe.query_report.invoices = invoices;
|
|
||||||
frappe.query_report.refresh();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"from_date",
|
"fieldname":"from_date",
|
||||||
@ -96,23 +33,5 @@ frappe.query_reports["TDS Payable Monthly"] = {
|
|||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"width": "60px"
|
"width": "60px"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
|
|
||||||
onload: function(report) {
|
|
||||||
// fetch all tds applied invoices
|
|
||||||
frappe.call({
|
|
||||||
"method": "erpnext.accounts.report.tds_payable_monthly.tds_payable_monthly.get_tds_invoices_and_orders",
|
|
||||||
callback: function(r) {
|
|
||||||
let invoices = [];
|
|
||||||
|
|
||||||
r.message.map(d => {
|
|
||||||
invoices.push(d.name);
|
|
||||||
});
|
|
||||||
|
|
||||||
report["invoice_data"] = r.message.invoices;
|
|
||||||
report["invoices"] = invoices;
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
{
|
{
|
||||||
"add_total_row": 1,
|
"add_total_row": 1,
|
||||||
|
"columns": [],
|
||||||
"creation": "2018-08-21 11:32:30.874923",
|
"creation": "2018-08-21 11:32:30.874923",
|
||||||
"disable_prepared_report": 0,
|
"disable_prepared_report": 0,
|
||||||
"disabled": 0,
|
"disabled": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Report",
|
"doctype": "Report",
|
||||||
|
"filters": [],
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": "Yes",
|
"is_standard": "Yes",
|
||||||
"modified": "2019-09-24 13:46:16.473711",
|
"modified": "2021-09-20 12:05:50.387572",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "TDS Payable Monthly",
|
"name": "TDS Payable Monthly",
|
||||||
|
@ -8,19 +8,12 @@ from frappe import _
|
|||||||
|
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
filters["invoices"] = frappe.cache().hget("invoices", frappe.session.user)
|
|
||||||
validate_filters(filters)
|
validate_filters(filters)
|
||||||
set_filters(filters)
|
tds_docs, tds_accounts, tax_category_map = get_tds_docs(filters)
|
||||||
|
|
||||||
# TDS payment entries
|
|
||||||
payment_entries = get_payment_entires(filters)
|
|
||||||
|
|
||||||
columns = get_columns(filters)
|
columns = get_columns(filters)
|
||||||
if not filters.get("invoices"):
|
|
||||||
return columns, []
|
|
||||||
|
|
||||||
res = get_result(filters, payment_entries)
|
|
||||||
|
|
||||||
|
res = get_result(filters, tds_docs, tds_accounts, tax_category_map)
|
||||||
return columns, res
|
return columns, res
|
||||||
|
|
||||||
def validate_filters(filters):
|
def validate_filters(filters):
|
||||||
@ -28,109 +21,59 @@ def validate_filters(filters):
|
|||||||
if filters.from_date > filters.to_date:
|
if filters.from_date > filters.to_date:
|
||||||
frappe.throw(_("From Date must be before To Date"))
|
frappe.throw(_("From Date must be before To Date"))
|
||||||
|
|
||||||
def set_filters(filters):
|
def get_result(filters, tds_docs, tds_accounts, tax_category_map):
|
||||||
invoices = []
|
supplier_map = get_supplier_pan_map()
|
||||||
|
tax_rate_map = get_tax_rate_map(filters)
|
||||||
if not filters.get("invoices"):
|
gle_map = get_gle_map(filters, tds_docs)
|
||||||
filters["invoices"] = get_tds_invoices_and_orders()
|
|
||||||
|
|
||||||
if filters.supplier and filters.purchase_invoice:
|
|
||||||
for d in filters["invoices"]:
|
|
||||||
if d.name == filters.purchase_invoice and d.supplier == filters.supplier:
|
|
||||||
invoices.append(d)
|
|
||||||
elif filters.supplier and not filters.purchase_invoice:
|
|
||||||
for d in filters["invoices"]:
|
|
||||||
if d.supplier == filters.supplier:
|
|
||||||
invoices.append(d)
|
|
||||||
elif filters.purchase_invoice and not filters.supplier:
|
|
||||||
for d in filters["invoices"]:
|
|
||||||
if d.name == filters.purchase_invoice:
|
|
||||||
invoices.append(d)
|
|
||||||
elif filters.supplier and filters.purchase_order:
|
|
||||||
for d in filters.get("invoices"):
|
|
||||||
if d.name == filters.purchase_order and d.supplier == filters.supplier:
|
|
||||||
invoices.append(d)
|
|
||||||
elif filters.supplier and not filters.purchase_order:
|
|
||||||
for d in filters.get("invoices"):
|
|
||||||
if d.supplier == filters.supplier:
|
|
||||||
invoices.append(d)
|
|
||||||
elif filters.purchase_order and not filters.supplier:
|
|
||||||
for d in filters.get("invoices"):
|
|
||||||
if d.name == filters.purchase_order:
|
|
||||||
invoices.append(d)
|
|
||||||
|
|
||||||
filters["invoices"] = invoices if invoices else filters["invoices"]
|
|
||||||
filters.naming_series = frappe.db.get_single_value('Buying Settings', 'supp_master_name')
|
|
||||||
|
|
||||||
#print(filters.get('invoices'))
|
|
||||||
|
|
||||||
def get_result(filters, payment_entries):
|
|
||||||
supplier_map, tds_docs = get_supplier_map(filters, payment_entries)
|
|
||||||
documents = [d.get('name') for d in filters.get('invoices')] + [d.get('name') for d in payment_entries]
|
|
||||||
|
|
||||||
gle_map = get_gle_map(filters, documents)
|
|
||||||
|
|
||||||
out = []
|
out = []
|
||||||
for d in gle_map:
|
for name, details in gle_map.items():
|
||||||
tds_deducted, total_amount_credited = 0, 0
|
tds_deducted, total_amount_credited = 0, 0
|
||||||
supplier = supplier_map[d]
|
tax_withholding_category = tax_category_map.get(name)
|
||||||
|
rate = tax_rate_map.get(tax_withholding_category)
|
||||||
|
|
||||||
tds_doc = tds_docs[supplier.tax_withholding_category]
|
for entry in details:
|
||||||
account_list = [i.account for i in tds_doc.accounts if i.company == filters.company]
|
supplier = entry.party or entry.against
|
||||||
|
posting_date = entry.posting_date
|
||||||
|
voucher_type = entry.voucher_type
|
||||||
|
|
||||||
if account_list:
|
if entry.account in tds_accounts:
|
||||||
account = account_list[0]
|
tds_deducted += (entry.credit - entry.debit)
|
||||||
|
|
||||||
for k in gle_map[d]:
|
total_amount_credited += (entry.credit - entry.debit)
|
||||||
if k.party == supplier_map[d] and k.credit > 0:
|
|
||||||
total_amount_credited += (k.credit - k.debit)
|
|
||||||
elif account_list and k.account == account and (k.credit - k.debit) > 0:
|
|
||||||
tds_deducted = (k.credit - k.debit)
|
|
||||||
total_amount_credited += (k.credit - k.debit)
|
|
||||||
voucher_type = k.voucher_type
|
|
||||||
|
|
||||||
rate = [i.tax_withholding_rate for i in tds_doc.rates
|
if rate and tds_deducted:
|
||||||
if i.fiscal_year == gle_map[d][0].fiscal_year]
|
row = {
|
||||||
|
'pan' if frappe.db.has_column('Supplier', 'pan') else 'tax_id': supplier_map.get(supplier).pan,
|
||||||
if rate and len(rate) > 0 and tds_deducted:
|
'supplier': supplier_map.get(supplier).name
|
||||||
rate = rate[0]
|
}
|
||||||
|
|
||||||
row = [supplier.pan, supplier.name]
|
|
||||||
|
|
||||||
if filters.naming_series == 'Naming Series':
|
if filters.naming_series == 'Naming Series':
|
||||||
row.append(supplier.supplier_name)
|
row.update({'supplier_name': supplier_map.get(supplier).supplier_name})
|
||||||
|
|
||||||
|
row.update({
|
||||||
|
'section_code': tax_withholding_category,
|
||||||
|
'entity_type': supplier_map.get(supplier).supplier_type,
|
||||||
|
'tds_rate': rate,
|
||||||
|
'total_amount_credited': total_amount_credited,
|
||||||
|
'tds_deducted': tds_deducted,
|
||||||
|
'transaction_date': posting_date,
|
||||||
|
'transaction_type': voucher_type,
|
||||||
|
'ref_no': name
|
||||||
|
})
|
||||||
|
|
||||||
row.extend([tds_doc.name, supplier.supplier_type, rate, total_amount_credited,
|
|
||||||
tds_deducted, gle_map[d][0].posting_date, voucher_type, d])
|
|
||||||
out.append(row)
|
out.append(row)
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def get_supplier_map(filters, payment_entries):
|
def get_supplier_pan_map():
|
||||||
# create a supplier_map of the form {"purchase_invoice": {supplier_name, pan, tds_name}}
|
supplier_map = frappe._dict()
|
||||||
# pre-fetch all distinct applicable tds docs
|
suppliers = frappe.db.get_all('Supplier', fields=['name', 'pan', 'supplier_type', 'supplier_name'])
|
||||||
supplier_map, tds_docs = {}, {}
|
|
||||||
pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id"
|
|
||||||
supplier_list = [d.supplier for d in filters["invoices"]]
|
|
||||||
|
|
||||||
supplier_detail = frappe.db.get_all('Supplier',
|
for d in suppliers:
|
||||||
{"name": ["in", supplier_list]},
|
supplier_map[d.name] = d
|
||||||
["tax_withholding_category", "name", pan+" as pan", "supplier_type", "supplier_name"])
|
|
||||||
|
|
||||||
for d in filters["invoices"]:
|
return supplier_map
|
||||||
supplier_map[d.get("name")] = [k for k in supplier_detail
|
|
||||||
if k.name == d.get("supplier")][0]
|
|
||||||
|
|
||||||
for d in payment_entries:
|
|
||||||
supplier_map[d.get("name")] = [k for k in supplier_detail
|
|
||||||
if k.name == d.get("supplier")][0]
|
|
||||||
|
|
||||||
for d in supplier_detail:
|
|
||||||
if d.get("tax_withholding_category") not in tds_docs:
|
|
||||||
tds_docs[d.get("tax_withholding_category")] = \
|
|
||||||
frappe.get_doc("Tax Withholding Category", d.get("tax_withholding_category"))
|
|
||||||
|
|
||||||
return supplier_map, tds_docs
|
|
||||||
|
|
||||||
def get_gle_map(filters, documents):
|
def get_gle_map(filters, documents):
|
||||||
# create gle_map of the form
|
# create gle_map of the form
|
||||||
@ -140,10 +83,9 @@ def get_gle_map(filters, documents):
|
|||||||
gle = frappe.db.get_all('GL Entry',
|
gle = frappe.db.get_all('GL Entry',
|
||||||
{
|
{
|
||||||
"voucher_no": ["in", documents],
|
"voucher_no": ["in", documents],
|
||||||
'is_cancelled': 0,
|
"credit": (">", 0)
|
||||||
'posting_date': ("between", [filters.get('from_date'), filters.get('to_date')]),
|
|
||||||
},
|
},
|
||||||
["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date", "voucher_type"],
|
["credit", "debit", "account", "voucher_no", "posting_date", "voucher_type", "against", "party"],
|
||||||
)
|
)
|
||||||
|
|
||||||
for d in gle:
|
for d in gle:
|
||||||
@ -233,39 +175,57 @@ def get_columns(filters):
|
|||||||
|
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
def get_payment_entires(filters):
|
def get_tds_docs(filters):
|
||||||
filter_dict = {
|
tds_documents = []
|
||||||
'posting_date': ("between", [filters.get('from_date'), filters.get('to_date')]),
|
purchase_invoices = []
|
||||||
'party_type': 'Supplier',
|
payment_entries = []
|
||||||
'apply_tax_withholding_amount': 1
|
journal_entries = []
|
||||||
|
tax_category_map = {}
|
||||||
|
|
||||||
|
tds_accounts = frappe.get_all("Tax Withholding Account", {'company': filters.get('company')},
|
||||||
|
pluck="account")
|
||||||
|
|
||||||
|
query_filters = {
|
||||||
|
"credit": ('>', 0),
|
||||||
|
"account": ("in", tds_accounts),
|
||||||
|
"posting_date": ("between", [filters.get("from_date"), filters.get("to_date")]),
|
||||||
|
"is_cancelled": 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if filters.get('purchase_invoice') or filters.get('purchase_order'):
|
if filters.get('supplier'):
|
||||||
parent = frappe.db.get_all('Payment Entry Reference',
|
query_filters.update({'against': filters.get('supplier')})
|
||||||
{'reference_name': ('in', [d.get('name') for d in filters.get('invoices')])}, ['parent'])
|
|
||||||
|
|
||||||
filter_dict.update({'name': ('in', [d.get('parent') for d in parent])})
|
tds_docs = frappe.get_all("GL Entry", query_filters, ["voucher_no", "voucher_type", "against", "party"])
|
||||||
|
|
||||||
payment_entries = frappe.get_all('Payment Entry', fields=['name', 'party_name as supplier'],
|
for d in tds_docs:
|
||||||
filters=filter_dict)
|
if d.voucher_type == "Purchase Invoice":
|
||||||
|
purchase_invoices.append(d.voucher_no)
|
||||||
|
elif d.voucher_type == "Payment Entry":
|
||||||
|
payment_entries.append(d.voucher_no)
|
||||||
|
elif d.voucher_type == "Journal Entry":
|
||||||
|
journal_entries.append(d.voucher_no)
|
||||||
|
|
||||||
return payment_entries
|
tds_documents.append(d.voucher_no)
|
||||||
|
|
||||||
@frappe.whitelist()
|
if purchase_invoices:
|
||||||
def get_tds_invoices_and_orders():
|
get_tax_category_map(purchase_invoices, 'Purchase Invoice', tax_category_map)
|
||||||
# fetch tds applicable supplier and fetch invoices for these suppliers
|
|
||||||
suppliers = [d.name for d in frappe.db.get_list("Supplier",
|
|
||||||
{"tax_withholding_category": ["!=", ""]}, ["name"])]
|
|
||||||
|
|
||||||
invoices = frappe.db.get_list("Purchase Invoice",
|
if payment_entries:
|
||||||
{"supplier": ["in", suppliers]}, ["name", "supplier"])
|
get_tax_category_map(payment_entries, 'Payment Entry', tax_category_map)
|
||||||
|
|
||||||
orders = frappe.db.get_list("Purchase Order",
|
if journal_entries:
|
||||||
{"supplier": ["in", suppliers]}, ["name", "supplier"])
|
get_tax_category_map(journal_entries, 'Journal Entry', tax_category_map)
|
||||||
|
|
||||||
invoices = invoices + orders
|
return tds_documents, tds_accounts, tax_category_map
|
||||||
invoices = [d for d in invoices if d.supplier]
|
|
||||||
|
|
||||||
frappe.cache().hset("invoices", frappe.session.user, invoices)
|
def get_tax_category_map(vouchers, doctype, tax_category_map):
|
||||||
|
tax_category_map.update(frappe._dict(frappe.get_all(doctype,
|
||||||
|
filters = {'name': ('in', vouchers)}, fields=['name', 'tax_withholding_category'], as_list=1)))
|
||||||
|
|
||||||
return invoices
|
def get_tax_rate_map(filters):
|
||||||
|
rate_map = frappe.get_all('Tax Withholding Rate', filters={
|
||||||
|
'from_date': ('<=', filters.get('from_date')),
|
||||||
|
'to_date': ('>=', filters.get('to_date'))
|
||||||
|
}, fields=['parent', 'tax_withholding_rate'], as_list=1)
|
||||||
|
|
||||||
|
return frappe._dict(rate_map)
|
@ -685,13 +685,17 @@ class AccountsController(TransactionBase):
|
|||||||
.format(d.reference_name, d.against_order))
|
.format(d.reference_name, d.against_order))
|
||||||
|
|
||||||
def set_advance_gain_or_loss(self):
|
def set_advance_gain_or_loss(self):
|
||||||
if not self.get("advances"):
|
if self.get('conversion_rate') == 1 or not self.get("advances"):
|
||||||
|
return
|
||||||
|
|
||||||
|
is_purchase_invoice = self.doctype == 'Purchase Invoice'
|
||||||
|
party_account = self.credit_to if is_purchase_invoice else self.debit_to
|
||||||
|
if get_account_currency(party_account) != self.currency:
|
||||||
return
|
return
|
||||||
|
|
||||||
for d in self.get("advances"):
|
for d in self.get("advances"):
|
||||||
advance_exchange_rate = d.ref_exchange_rate
|
advance_exchange_rate = d.ref_exchange_rate
|
||||||
if (d.allocated_amount and self.conversion_rate != 1
|
if (d.allocated_amount and self.conversion_rate != advance_exchange_rate):
|
||||||
and self.conversion_rate != advance_exchange_rate):
|
|
||||||
|
|
||||||
base_allocated_amount_in_ref_rate = advance_exchange_rate * d.allocated_amount
|
base_allocated_amount_in_ref_rate = advance_exchange_rate * d.allocated_amount
|
||||||
base_allocated_amount_in_inv_rate = self.conversion_rate * d.allocated_amount
|
base_allocated_amount_in_inv_rate = self.conversion_rate * d.allocated_amount
|
||||||
@ -710,7 +714,7 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
gain_loss_account = frappe.db.get_value('Company', self.company, 'exchange_gain_loss_account')
|
gain_loss_account = frappe.db.get_value('Company', self.company, 'exchange_gain_loss_account')
|
||||||
if not gain_loss_account:
|
if not gain_loss_account:
|
||||||
frappe.throw(_("Please set Default Exchange Gain/Loss Account in Company {}")
|
frappe.throw(_("Please set default Exchange Gain/Loss Account in Company {}")
|
||||||
.format(self.get('company')))
|
.format(self.get('company')))
|
||||||
account_currency = get_account_currency(gain_loss_account)
|
account_currency = get_account_currency(gain_loss_account)
|
||||||
if account_currency != self.company_currency:
|
if account_currency != self.company_currency:
|
||||||
@ -729,7 +733,7 @@ class AccountsController(TransactionBase):
|
|||||||
"against": party,
|
"against": party,
|
||||||
dr_or_cr + "_in_account_currency": abs(d.exchange_gain_loss),
|
dr_or_cr + "_in_account_currency": abs(d.exchange_gain_loss),
|
||||||
dr_or_cr: abs(d.exchange_gain_loss),
|
dr_or_cr: abs(d.exchange_gain_loss),
|
||||||
"cost_center": self.cost_center,
|
"cost_center": self.cost_center or erpnext.get_default_cost_center(self.company),
|
||||||
"project": self.project
|
"project": self.project
|
||||||
}, item=d)
|
}, item=d)
|
||||||
)
|
)
|
||||||
@ -1681,14 +1685,18 @@ def get_advance_payment_entries(party_type, party, party_account, order_doctype,
|
|||||||
return list(payment_entries_against_order) + list(unallocated_payment_entries)
|
return list(payment_entries_against_order) + list(unallocated_payment_entries)
|
||||||
|
|
||||||
def update_invoice_status():
|
def update_invoice_status():
|
||||||
# Daily update the status of the invoices
|
"""Updates status as Overdue for applicable invoices. Runs daily."""
|
||||||
|
|
||||||
frappe.db.sql(""" update `tabSales Invoice` set status = 'Overdue'
|
|
||||||
where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
|
|
||||||
|
|
||||||
frappe.db.sql(""" update `tabPurchase Invoice` set status = 'Overdue'
|
|
||||||
where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
|
|
||||||
|
|
||||||
|
for doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||||
|
frappe.db.sql("""
|
||||||
|
update `tab{}` as dt set dt.status = 'Overdue'
|
||||||
|
where dt.docstatus = 1
|
||||||
|
and dt.status != 'Overdue'
|
||||||
|
and dt.outstanding_amount > 0
|
||||||
|
and (dt.grand_total - dt.outstanding_amount) <
|
||||||
|
(select sum(payment_amount) from `tabPayment Schedule` as ps
|
||||||
|
where ps.parent = dt.name and ps.due_date < %s)
|
||||||
|
""".format(doctype), getdate())
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_payment_terms(terms_template, posting_date=None, grand_total=None, base_grand_total=None, bill_date=None):
|
def get_payment_terms(terms_template, posting_date=None, grand_total=None, base_grand_total=None, bill_date=None):
|
||||||
|
@ -184,7 +184,7 @@ def get_employees_having_an_event_today(event_type):
|
|||||||
# --------------------------
|
# --------------------------
|
||||||
def send_work_anniversary_reminders():
|
def send_work_anniversary_reminders():
|
||||||
"""Send Employee Work Anniversary Reminders if 'Send Work Anniversary Reminders' is checked"""
|
"""Send Employee Work Anniversary Reminders if 'Send Work Anniversary Reminders' is checked"""
|
||||||
to_send = int(frappe.db.get_single_value("HR Settings", "send_work_anniversary_reminders") or 1)
|
to_send = int(frappe.db.get_single_value("HR Settings", "send_work_anniversary_reminders"))
|
||||||
if not to_send:
|
if not to_send:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ frappe.ui.form.on('Training Result', {
|
|||||||
frm.set_value("employees" ,"");
|
frm.set_value("employees" ,"");
|
||||||
if (r.message) {
|
if (r.message) {
|
||||||
$.each(r.message, function(i, d) {
|
$.each(r.message, function(i, d) {
|
||||||
var row = frappe.model.add_child(cur_frm.doc, "Training Result Employee", "employees");
|
var row = frappe.model.add_child(frm.doc, "Training Result Employee", "employees");
|
||||||
row.employee = d.employee;
|
row.employee = d.employee;
|
||||||
row.employee_name = d.employee_name;
|
row.employee_name = d.employee_name;
|
||||||
});
|
});
|
||||||
|
@ -677,7 +677,7 @@ def get_job_details(start, end, filters=None):
|
|||||||
conditions = get_filters_cond("Job Card", filters, [])
|
conditions = get_filters_cond("Job Card", filters, [])
|
||||||
|
|
||||||
job_cards = frappe.db.sql(""" SELECT `tabJob Card`.name, `tabJob Card`.work_order,
|
job_cards = frappe.db.sql(""" SELECT `tabJob Card`.name, `tabJob Card`.work_order,
|
||||||
`tabJob Card`.employee_name, `tabJob Card`.status, ifnull(`tabJob Card`.remarks, ''),
|
`tabJob Card`.status, ifnull(`tabJob Card`.remarks, ''),
|
||||||
min(`tabJob Card Time Log`.from_time) as from_time,
|
min(`tabJob Card Time Log`.from_time) as from_time,
|
||||||
max(`tabJob Card Time Log`.to_time) as to_time
|
max(`tabJob Card Time Log`.to_time) as to_time
|
||||||
FROM `tabJob Card` , `tabJob Card Time Log`
|
FROM `tabJob Card` , `tabJob Card Time Log`
|
||||||
@ -687,7 +687,7 @@ def get_job_details(start, end, filters=None):
|
|||||||
|
|
||||||
for d in job_cards:
|
for d in job_cards:
|
||||||
subject_data = []
|
subject_data = []
|
||||||
for field in ["name", "work_order", "remarks", "employee_name"]:
|
for field in ["name", "work_order", "remarks"]:
|
||||||
if not d.get(field): continue
|
if not d.get(field): continue
|
||||||
|
|
||||||
subject_data.append(d.get(field))
|
subject_data.append(d.get(field))
|
||||||
|
@ -457,7 +457,8 @@ class ProductionPlan(Document):
|
|||||||
|
|
||||||
def prepare_args_for_sub_assembly_items(self, row, args):
|
def prepare_args_for_sub_assembly_items(self, row, args):
|
||||||
for field in ["production_item", "item_name", "qty", "fg_warehouse",
|
for field in ["production_item", "item_name", "qty", "fg_warehouse",
|
||||||
"description", "bom_no", "stock_uom", "bom_level", "production_plan_item"]:
|
"description", "bom_no", "stock_uom", "bom_level",
|
||||||
|
"production_plan_item", "schedule_date"]:
|
||||||
args[field] = row.get(field)
|
args[field] = row.get(field)
|
||||||
|
|
||||||
args.update({
|
args.update({
|
||||||
|
@ -309,3 +309,4 @@ erpnext.patches.v13_0.update_dates_in_tax_withholding_category
|
|||||||
erpnext.patches.v14_0.update_opportunity_currency_fields
|
erpnext.patches.v14_0.update_opportunity_currency_fields
|
||||||
erpnext.patches.v13_0.gst_fields_for_pos_invoice
|
erpnext.patches.v13_0.gst_fields_for_pos_invoice
|
||||||
erpnext.patches.v13_0.create_accounting_dimensions_in_pos_doctypes
|
erpnext.patches.v13_0.create_accounting_dimensions_in_pos_doctypes
|
||||||
|
erpnext.patches.v13_0.modify_invalid_gain_loss_gl_entries
|
49
erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py
Normal file
49
erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc('accounts', 'doctype', 'purchase_invoice_advance')
|
||||||
|
frappe.reload_doc('accounts', 'doctype', 'sales_invoice_advance')
|
||||||
|
|
||||||
|
purchase_invoices = frappe.db.sql("""
|
||||||
|
select
|
||||||
|
parenttype as type, parent as name
|
||||||
|
from
|
||||||
|
`tabPurchase Invoice Advance`
|
||||||
|
where
|
||||||
|
ref_exchange_rate = 1
|
||||||
|
and docstatus = 1
|
||||||
|
and ifnull(exchange_gain_loss, '') != ''
|
||||||
|
group by
|
||||||
|
parent
|
||||||
|
""", as_dict=1)
|
||||||
|
|
||||||
|
sales_invoices = frappe.db.sql("""
|
||||||
|
select
|
||||||
|
parenttype as type, parent as name
|
||||||
|
from
|
||||||
|
`tabSales Invoice Advance`
|
||||||
|
where
|
||||||
|
ref_exchange_rate = 1
|
||||||
|
and docstatus = 1
|
||||||
|
and ifnull(exchange_gain_loss, '') != ''
|
||||||
|
group by
|
||||||
|
parent
|
||||||
|
""", as_dict=1)
|
||||||
|
|
||||||
|
if purchase_invoices + sales_invoices:
|
||||||
|
frappe.log_error(json.dumps(purchase_invoices + sales_invoices, indent=2), title="Patch Log")
|
||||||
|
|
||||||
|
for invoice in purchase_invoices + sales_invoices:
|
||||||
|
doc = frappe.get_doc(invoice.type, invoice.name)
|
||||||
|
doc.docstatus = 2
|
||||||
|
doc.make_gl_entries()
|
||||||
|
for advance in doc.advances:
|
||||||
|
if advance.ref_exchange_rate == 1:
|
||||||
|
advance.db_set('exchange_gain_loss', 0, False)
|
||||||
|
doc.docstatus = 1
|
||||||
|
doc.make_gl_entries()
|
@ -752,7 +752,7 @@ def set_salary_components(docs):
|
|||||||
|
|
||||||
def set_tax_withholding_category(company):
|
def set_tax_withholding_category(company):
|
||||||
accounts = []
|
accounts = []
|
||||||
fiscal_year = None
|
fiscal_year_details = None
|
||||||
abbr = frappe.get_value("Company", company, "abbr")
|
abbr = frappe.get_value("Company", company, "abbr")
|
||||||
tds_account = frappe.get_value("Account", 'TDS Payable - {0}'.format(abbr), 'name')
|
tds_account = frappe.get_value("Account", 'TDS Payable - {0}'.format(abbr), 'name')
|
||||||
|
|
||||||
|
@ -2116,9 +2116,9 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"Saudi Arabia": {
|
"Saudi Arabia": {
|
||||||
"KSA VAT 5%": {
|
"KSA VAT 15%": {
|
||||||
"account_name": "VAT 5%",
|
"account_name": "VAT 15%",
|
||||||
"tax_rate": 5.00
|
"tax_rate": 15.00
|
||||||
},
|
},
|
||||||
"KSA VAT Zero": {
|
"KSA VAT Zero": {
|
||||||
"account_name": "VAT Zero",
|
"account_name": "VAT Zero",
|
||||||
|
@ -11,6 +11,6 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ol>
|
</ol>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>You don't have no upcoming holidays this {{ frequency }}.</p>
|
<p>You have no upcoming holidays this {{ frequency }}.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="col-xs-5 {%- if doc.align_labels_right %} text-right{%- endif -%}">
|
<div class="col-xs-5 {%- if doc.align_labels_right %} text-right{%- endif -%}">
|
||||||
<label>{{ _(doc.meta.get_label('total')) }}</label></div>
|
<label>{{ _(df.label) }}</label></div>
|
||||||
<div class="col-xs-7 text-right">
|
<div class="col-xs-7 text-right">
|
||||||
{{ doc.get_formatted("total", doc) }}
|
{{ doc.get_formatted("total", doc) }}
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user