Merge branch 'develop' into trim-custom-field-length

This commit is contained in:
Saqib 2021-09-29 19:56:56 +05:30 committed by GitHub
commit a84cd2674c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 526 additions and 387 deletions

View File

@ -1,63 +1,33 @@
{ {
"allow_copy": 0, "actions": [],
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2014-10-02 13:35:44.155278", "creation": "2014-10-02 13:35:44.155278",
"custom": 0,
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Setup", "document_type": "Setup",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"company"
],
"fields": [ "fields": [
{ {
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "company", "fieldname": "company",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "ignore_user_permissions": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"label": "Company", "label": "Company",
"length": 0, "options": "Company"
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
} }
], ],
"hide_heading": 0, "index_web_pages_for_search": 1,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "links": [],
"modified": "2016-07-11 03:28:00.505946", "modified": "2021-09-28 18:01:53.495929",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Fiscal Year Company", "name": "Fiscal Year Company",
"name_case": "",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"track_seen": 0 "track_changes": 1
} }

View File

@ -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": [
{ {

View File

@ -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,\

View File

@ -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",

View File

@ -1303,6 +1303,7 @@
"fetch_from": "supplier.is_internal_supplier", "fetch_from": "supplier.is_internal_supplier",
"fieldname": "is_internal_supplier", "fieldname": "is_internal_supplier",
"fieldtype": "Check", "fieldtype": "Check",
"ignore_user_permissions": 1,
"label": "Is Internal Supplier", "label": "Is Internal Supplier",
"read_only": 1 "read_only": 1
}, },
@ -1400,7 +1401,7 @@
"idx": 204, "idx": 204,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2021-09-21 09:27:39.967811", "modified": "2021-09-28 13:10:28.351810",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice", "name": "Purchase Invoice",

View File

@ -1146,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:

View File

@ -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",

View File

@ -1953,6 +1953,7 @@
"fetch_from": "customer.represents_company", "fetch_from": "customer.represents_company",
"fieldname": "represents_company", "fieldname": "represents_company",
"fieldtype": "Link", "fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Represents Company", "label": "Represents Company",
"options": "Company", "options": "Company",
"read_only": 1 "read_only": 1
@ -2022,7 +2023,7 @@
"link_fieldname": "consolidated_invoice" "link_fieldname": "consolidated_invoice"
} }
], ],
"modified": "2021-09-21 09:27:50.191854", "modified": "2021-09-28 13:09:34.391799",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice", "name": "Sales Invoice",

View File

@ -2352,6 +2352,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",
@ -2373,11 +2374,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",
@ -2397,9 +2399,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",
@ -2414,11 +2441,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():
@ -2458,7 +2480,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"

View File

@ -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",

View File

@ -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

View File

@ -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()

View File

@ -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",

View File

@ -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
}, },
{ {

View File

@ -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;
}
});
}
} }

View File

@ -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",

View File

@ -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)

View File

@ -394,10 +394,6 @@ class Asset(AccountsController):
if cint(self.number_of_depreciations_booked) > cint(row.total_number_of_depreciations): if cint(self.number_of_depreciations_booked) > cint(row.total_number_of_depreciations):
frappe.throw(_("Number of Depreciations Booked cannot be greater than Total Number of Depreciations")) frappe.throw(_("Number of Depreciations Booked cannot be greater than Total Number of Depreciations"))
if row.depreciation_start_date and getdate(row.depreciation_start_date) < getdate(nowdate()):
frappe.msgprint(_("Depreciation Row {0}: Depreciation Start Date is entered as past date")
.format(row.idx), title=_('Warning'), indicator='red')
if row.depreciation_start_date and getdate(row.depreciation_start_date) < getdate(self.purchase_date): if row.depreciation_start_date and getdate(row.depreciation_start_date) < getdate(self.purchase_date):
frappe.throw(_("Depreciation Row {0}: Next Depreciation Date cannot be before Purchase Date") frappe.throw(_("Depreciation Row {0}: Next Depreciation Date cannot be before Purchase Date")
.format(row.idx)) .format(row.idx))

View File

@ -645,12 +645,18 @@ class TestAsset(unittest.TestCase):
pr = make_purchase_receipt(item_code="Macbook Pro", pr = make_purchase_receipt(item_code="Macbook Pro",
qty=1, rate=8000.0, location="Test Location") qty=1, rate=8000.0, location="Test Location")
finance_book = frappe.new_doc('Finance Book')
finance_book.finance_book_name = 'Income Tax'
finance_book.for_income_tax = 1
finance_book.insert(ignore_if_duplicate=1)
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
asset = frappe.get_doc('Asset', asset_name) asset = frappe.get_doc('Asset', asset_name)
asset.calculate_depreciation = 1 asset.calculate_depreciation = 1
asset.available_for_use_date = '2030-07-12' asset.available_for_use_date = '2030-07-12'
asset.purchase_date = '2030-01-01' asset.purchase_date = '2030-01-01'
asset.append("finance_books", { asset.append("finance_books", {
"finance_book": finance_book.name,
"expected_value_after_useful_life": 1000, "expected_value_after_useful_life": 1000,
"depreciation_method": "Written Down Value", "depreciation_method": "Written Down Value",
"total_number_of_depreciations": 3, "total_number_of_depreciations": 3,

View File

@ -1121,6 +1121,7 @@
"fetch_from": "supplier.represents_company", "fetch_from": "supplier.represents_company",
"fieldname": "represents_company", "fieldname": "represents_company",
"fieldtype": "Link", "fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Represents Company", "label": "Represents Company",
"options": "Company", "options": "Company",
"read_only": 1 "read_only": 1
@ -1143,7 +1144,7 @@
"idx": 105, "idx": 105,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2021-08-30 20:03:14.008804", "modified": "2021-09-28 13:10:47.955401",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order", "name": "Purchase Order",

View File

@ -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)
) )

View File

@ -1133,7 +1133,8 @@ def item_query(doctype, txt, searchfield, start, page_len, filters):
query_filters["has_variants"] = 0 query_filters["has_variants"] = 0
if filters and filters.get("is_stock_item"): if filters and filters.get("is_stock_item"):
query_filters["is_stock_item"] = 1 or_cond_filters["is_stock_item"] = 1
or_cond_filters["has_variants"] = 1
return frappe.get_list("Item", return frappe.get_list("Item",
fields = fields, filters=query_filters, fields = fields, filters=query_filters,

View File

@ -310,3 +310,5 @@ 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.trim_sales_invoice_custom_field_length erpnext.patches.v13_0.trim_sales_invoice_custom_field_length
erpnext.patches.v13_0.create_custom_field_for_finance_book
erpnext.patches.v13_0.modify_invalid_gain_loss_gl_entries

View File

@ -0,0 +1,21 @@
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
return
custom_field = {
'Finance Book': [
{
'fieldname': 'for_income_tax',
'label': 'For Income Tax',
'fieldtype': 'Check',
'insert_after': 'finance_book_name',
'description': 'If the asset is put to use for less than 180 days, the first Depreciation Rate will be reduced by 50%.'
}
]
}
create_custom_fields(custom_field, update=1)

View 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()

View File

@ -144,6 +144,9 @@ class Project(Document):
if self.sales_order: if self.sales_order:
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name) frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
def on_trash(self):
frappe.db.set_value("Sales Order", {"project": self.name}, "project", "")
def update_percent_complete(self): def update_percent_complete(self):
if self.percent_complete_method == "Manual": if self.percent_complete_method == "Manual":
if self.status == "Completed": if self.status == "Completed":

View File

@ -9,6 +9,8 @@ from frappe.utils import add_days, getdate, nowdate
from erpnext.projects.doctype.project_template.test_project_template import make_project_template from erpnext.projects.doctype.project_template.test_project_template import make_project_template
from erpnext.projects.doctype.task.test_task import create_task from erpnext.projects.doctype.task.test_task import create_task
from erpnext.selling.doctype.sales_order.sales_order import make_project as make_project_from_so
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
test_records = frappe.get_test_records('Project') test_records = frappe.get_test_records('Project')
test_ignore = ["Sales Order"] test_ignore = ["Sales Order"]
@ -96,6 +98,21 @@ class TestProject(unittest.TestCase):
self.assertEqual(len(tasks), 2) self.assertEqual(len(tasks), 2)
def test_project_linking_with_sales_order(self):
so = make_sales_order()
project = make_project_from_so(so.name)
project.save()
self.assertEqual(project.sales_order, so.name)
so.reload()
self.assertEqual(so.project, project.name)
project.delete()
so.reload()
self.assertFalse(so.project)
def get_project(name, template): def get_project(name, template):
project = frappe.get_doc(dict( project = frappe.get_doc(dict(

View File

@ -617,6 +617,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
me.frm.script_manager.trigger('qty', item.doctype, item.name); me.frm.script_manager.trigger('qty', item.doctype, item.name);
if (!me.frm.doc.set_warehouse) if (!me.frm.doc.set_warehouse)
me.frm.script_manager.trigger('warehouse', item.doctype, item.name); me.frm.script_manager.trigger('warehouse', item.doctype, item.name);
me.apply_price_list(item, true);
}, undefined, !frappe.flags.hide_serial_batch_dialog); }, undefined, !frappe.flags.hide_serial_batch_dialog);
} }
}, },

View File

@ -674,6 +674,15 @@ def get_custom_fields():
'fieldtype': 'Data', 'fieldtype': 'Data',
'insert_after': 'email' 'insert_after': 'email'
} }
],
'Finance Book': [
{
'fieldname': 'for_income_tax',
'label': 'For Income Tax',
'fieldtype': 'Check',
'insert_after': 'finance_book_name',
'description': 'If the asset is put to use for less than 180 days, the first Depreciation Rate will be reduced by 50%.'
}
] ]
} }

View File

@ -857,12 +857,13 @@ def get_depreciation_amount(asset, depreciable_value, row):
rate_of_depreciation = row.rate_of_depreciation rate_of_depreciation = row.rate_of_depreciation
# if its the first depreciation # if its the first depreciation
if depreciable_value == asset.gross_purchase_amount: if depreciable_value == asset.gross_purchase_amount:
# as per IT act, if the asset is purchased in the 2nd half of fiscal year, then rate is divided by 2 if row.finance_book and frappe.db.get_value('Finance Book', row.finance_book, 'for_income_tax'):
diff = date_diff(row.depreciation_start_date, asset.available_for_use_date) # as per IT act, if the asset is purchased in the 2nd half of fiscal year, then rate is divided by 2
if diff <= 180: diff = date_diff(row.depreciation_start_date, asset.available_for_use_date)
rate_of_depreciation = rate_of_depreciation / 2 if diff <= 180:
frappe.msgprint( rate_of_depreciation = rate_of_depreciation / 2
_('As per IT Act, the rate of depreciation for the first depreciation entry is reduced by 50%.')) frappe.msgprint(
_('As per IT Act, the rate of depreciation for the first depreciation entry is reduced by 50%.'))
depreciation_amount = flt(depreciable_value * (flt(rate_of_depreciation) / 100)) depreciation_amount = flt(depreciable_value * (flt(rate_of_depreciation) / 100))

View File

@ -1480,6 +1480,7 @@
"fetch_from": "customer.represents_company", "fetch_from": "customer.represents_company",
"fieldname": "represents_company", "fieldname": "represents_company",
"fieldtype": "Link", "fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Represents Company", "label": "Represents Company",
"options": "Company", "options": "Company",
"read_only": 1 "read_only": 1
@ -1512,7 +1513,7 @@
"idx": 105, "idx": 105,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2021-09-01 15:12:24.115483", "modified": "2021-09-28 13:09:51.515542",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Order", "name": "Sales Order",

View File

@ -1277,6 +1277,7 @@
"fetch_from": "customer.represents_company", "fetch_from": "customer.represents_company",
"fieldname": "represents_company", "fieldname": "represents_company",
"fieldtype": "Link", "fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Represents Company", "label": "Represents Company",
"options": "Company", "options": "Company",
"read_only": 1 "read_only": 1
@ -1308,7 +1309,7 @@
"idx": 146, "idx": 146,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2021-08-27 20:14:40.215231", "modified": "2021-09-28 13:10:09.761714",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Delivery Note", "name": "Delivery Note",

View File

@ -36,7 +36,8 @@
"fieldname": "qty", "fieldname": "qty",
"fieldtype": "Float", "fieldtype": "Float",
"in_list_view": 1, "in_list_view": 1,
"label": "Qty" "label": "Qty",
"reqd": 1
}, },
{ {
"fieldname": "picked_qty", "fieldname": "picked_qty",
@ -180,7 +181,7 @@
], ],
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-06-24 17:18:57.357120", "modified": "2021-09-28 12:02:16.923056",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Pick List Item", "name": "Pick List Item",

View File

@ -1140,6 +1140,7 @@
"fetch_from": "supplier.represents_company", "fetch_from": "supplier.represents_company",
"fieldname": "represents_company", "fieldname": "represents_company",
"fieldtype": "Link", "fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Represents Company", "label": "Represents Company",
"options": "Company", "options": "Company",
"read_only": 1 "read_only": 1
@ -1149,7 +1150,7 @@
"idx": 261, "idx": 261,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2021-08-17 20:16:40.849885", "modified": "2021-09-28 13:11:10.181328",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Purchase Receipt", "name": "Purchase Receipt",

View File

@ -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>