fix: conflicts
This commit is contained in:
@@ -5,7 +5,7 @@ import frappe
|
|||||||
from erpnext.hooks import regional_overrides
|
from erpnext.hooks import regional_overrides
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate
|
||||||
|
|
||||||
__version__ = '13.7.1'
|
__version__ = '13.8.0'
|
||||||
|
|
||||||
def get_default_company(user=None):
|
def get_default_company(user=None):
|
||||||
'''Get default company for user'''
|
'''Get default company for user'''
|
||||||
|
|||||||
@@ -391,5 +391,5 @@ def set_default_accounts(company):
|
|||||||
})
|
})
|
||||||
|
|
||||||
company.save()
|
company.save()
|
||||||
install_country_fixtures(company.name)
|
install_country_fixtures(company.name, company.country)
|
||||||
company.create_default_tax_template()
|
company.create_default_tax_template()
|
||||||
|
|||||||
@@ -306,5 +306,5 @@ def reconcile_dr_cr_note(dr_cr_notes, company):
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
jv.flags.ignore_mandatory = True
|
||||||
jv.submit()
|
jv.submit()
|
||||||
@@ -168,7 +168,7 @@ def _get_tree_conditions(args, parenttype, table, allow_blank=True):
|
|||||||
frappe.throw(_("Invalid {0}").format(args.get(field)))
|
frappe.throw(_("Invalid {0}").format(args.get(field)))
|
||||||
|
|
||||||
parent_groups = frappe.db.sql_list("""select name from `tab%s`
|
parent_groups = frappe.db.sql_list("""select name from `tab%s`
|
||||||
where lft>=%s and rgt<=%s""" % (parenttype, '%s', '%s'), (lft, rgt))
|
where lft<=%s and rgt>=%s""" % (parenttype, '%s', '%s'), (lft, rgt))
|
||||||
|
|
||||||
if parenttype in ["Customer Group", "Item Group", "Territory"]:
|
if parenttype in ["Customer Group", "Item Group", "Territory"]:
|
||||||
parent_field = "parent_{0}".format(frappe.scrub(parenttype))
|
parent_field = "parent_{0}".format(frappe.scrub(parenttype))
|
||||||
|
|||||||
@@ -1018,8 +1018,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
expected_gle = [
|
expected_gle = [
|
||||||
["_Test Account Cost for Goods Sold - _TC", 37500.0],
|
["_Test Account Cost for Goods Sold - _TC", 37500.0],
|
||||||
["_Test Payable USD - _TC", -40000.0],
|
["_Test Payable USD - _TC", -35000.0],
|
||||||
["Exchange Gain/Loss - _TC", 2500.0]
|
["Exchange Gain/Loss - _TC", -2500.0]
|
||||||
]
|
]
|
||||||
|
|
||||||
gl_entries = frappe.db.sql("""
|
gl_entries = frappe.db.sql("""
|
||||||
@@ -1049,8 +1049,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
expected_gle = [
|
expected_gle = [
|
||||||
["_Test Account Cost for Goods Sold - _TC", 36500.0],
|
["_Test Account Cost for Goods Sold - _TC", 36500.0],
|
||||||
["_Test Payable USD - _TC", -38000.0],
|
["_Test Payable USD - _TC", -35000.0],
|
||||||
["Exchange Gain/Loss - _TC", 1500.0]
|
["Exchange Gain/Loss - _TC", -1500.0]
|
||||||
]
|
]
|
||||||
|
|
||||||
gl_entries = frappe.db.sql("""
|
gl_entries = frappe.db.sql("""
|
||||||
|
|||||||
@@ -1,263 +1,151 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
"allow_rename": 1,
|
||||||
"autoname": "Prompt",
|
"autoname": "Prompt",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2018-04-13 18:42:06.431683",
|
"creation": "2018-04-13 18:42:06.431683",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"category_details_section",
|
||||||
|
"category_name",
|
||||||
|
"round_off_tax_amount",
|
||||||
|
"column_break_2",
|
||||||
|
"consider_party_ledger_amount",
|
||||||
|
"tax_on_excess_amount",
|
||||||
|
"section_break_8",
|
||||||
|
"rates",
|
||||||
|
"section_break_7",
|
||||||
|
"accounts"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "category_name",
|
"fieldname": "category_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Category Name",
|
"label": "Category Name",
|
||||||
"length": 0,
|
"show_days": 1,
|
||||||
"no_copy": 0,
|
"show_seconds": 1
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break_8",
|
"fieldname": "section_break_8",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Tax Withholding Rates",
|
"label": "Tax Withholding Rates",
|
||||||
"length": 0,
|
"show_days": 1,
|
||||||
"no_copy": 0,
|
"show_seconds": 1
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "rates",
|
"fieldname": "rates",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Rates",
|
"label": "Rates",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Tax Withholding Rate",
|
"options": "Tax Withholding Rate",
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"show_days": 1,
|
||||||
"set_only_once": 0,
|
"show_seconds": 1
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break_7",
|
"fieldname": "section_break_7",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Account Details",
|
"label": "Account Details",
|
||||||
"length": 0,
|
"show_days": 1,
|
||||||
"no_copy": 0,
|
"show_seconds": 1
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "accounts",
|
"fieldname": "accounts",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Accounts",
|
"label": "Accounts",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Tax Withholding Account",
|
"options": "Tax Withholding Account",
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"show_days": 1,
|
||||||
"set_only_once": 0,
|
"show_seconds": 1
|
||||||
"translatable": 0,
|
},
|
||||||
"unique": 0
|
{
|
||||||
|
"fieldname": "category_details_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Category Details",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_2",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"description": "Even invoices with apply tax withholding unchecked will be considered for checking cumulative threshold breach",
|
||||||
|
"fieldname": "consider_party_ledger_amount",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Consider Entire Party Ledger Amount",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"description": "Tax will be withheld only for amount exceeding the cumulative threshold",
|
||||||
|
"fieldname": "tax_on_excess_amount",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Only Deduct Tax On Excess Amount ",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Checking this will round off the tax amount to the nearest integer",
|
||||||
|
"fieldname": "round_off_tax_amount",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Round Off Tax Amount",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"index_web_pages_for_search": 1,
|
||||||
"hide_heading": 0,
|
"links": [],
|
||||||
"hide_toolbar": 0,
|
"modified": "2021-07-27 21:47:34.396071",
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2018-07-17 22:53:26.193179",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Tax Withholding Category",
|
"name": "Tax Withholding Category",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "System Manager",
|
"role": "System Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts Manager",
|
"role": "Accounts Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts User",
|
"role": "Accounts User",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import flt, getdate
|
from frappe.utils import flt, getdate, cint
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
|
|
||||||
class TaxWithholdingCategory(Document):
|
class TaxWithholdingCategory(Document):
|
||||||
@@ -86,7 +86,10 @@ def get_tax_withholding_details(tax_withholding_category, fiscal_year, company):
|
|||||||
"rate": tax_rate_detail.tax_withholding_rate,
|
"rate": tax_rate_detail.tax_withholding_rate,
|
||||||
"threshold": tax_rate_detail.single_threshold,
|
"threshold": tax_rate_detail.single_threshold,
|
||||||
"cumulative_threshold": tax_rate_detail.cumulative_threshold,
|
"cumulative_threshold": tax_rate_detail.cumulative_threshold,
|
||||||
"description": tax_withholding.category_name if tax_withholding.category_name else tax_withholding_category
|
"description": tax_withholding.category_name if tax_withholding.category_name else tax_withholding_category,
|
||||||
|
"consider_party_ledger_amount": tax_withholding.consider_party_ledger_amount,
|
||||||
|
"tax_on_excess_amount": tax_withholding.tax_on_excess_amount,
|
||||||
|
"round_off_tax_amount": tax_withholding.round_off_tax_amount
|
||||||
})
|
})
|
||||||
|
|
||||||
def get_tax_withholding_rates(tax_withholding, fiscal_year):
|
def get_tax_withholding_rates(tax_withholding, fiscal_year):
|
||||||
@@ -145,6 +148,7 @@ def get_lower_deduction_certificate(fiscal_year, pan_no):
|
|||||||
def get_tax_amount(party_type, parties, inv, tax_details, fiscal_year_details, pan_no=None):
|
def get_tax_amount(party_type, parties, inv, tax_details, fiscal_year_details, pan_no=None):
|
||||||
fiscal_year = fiscal_year_details[0]
|
fiscal_year = fiscal_year_details[0]
|
||||||
|
|
||||||
|
|
||||||
vouchers = get_invoice_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
|
vouchers = get_invoice_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
|
||||||
advance_vouchers = get_advance_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
|
advance_vouchers = get_advance_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
|
||||||
taxable_vouchers = vouchers + advance_vouchers
|
taxable_vouchers = vouchers + advance_vouchers
|
||||||
@@ -235,10 +239,18 @@ def get_deducted_tax(taxable_vouchers, fiscal_year, tax_details):
|
|||||||
|
|
||||||
def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_deducted, vouchers):
|
def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_deducted, vouchers):
|
||||||
tds_amount = 0
|
tds_amount = 0
|
||||||
|
invoice_filters = {
|
||||||
|
'name': ('in', vouchers),
|
||||||
|
'docstatus': 1
|
||||||
|
}
|
||||||
|
|
||||||
supp_credit_amt = frappe.db.get_value('Purchase Invoice', {
|
field = 'sum(net_total)'
|
||||||
'name': ('in', vouchers), 'docstatus': 1, 'apply_tds': 1
|
|
||||||
}, 'sum(net_total)') or 0.0
|
if not cint(tax_details.consider_party_ledger_amount):
|
||||||
|
invoice_filters.update({'apply_tds': 1})
|
||||||
|
field = 'sum(grand_total)'
|
||||||
|
|
||||||
|
supp_credit_amt = frappe.db.get_value('Purchase Invoice', invoice_filters, field) or 0.0
|
||||||
|
|
||||||
supp_jv_credit_amt = frappe.db.get_value('Journal Entry Account', {
|
supp_jv_credit_amt = frappe.db.get_value('Journal Entry Account', {
|
||||||
'parent': ('in', vouchers), 'docstatus': 1,
|
'parent': ('in', vouchers), 'docstatus': 1,
|
||||||
@@ -255,6 +267,13 @@ def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_dedu
|
|||||||
cumulative_threshold = tax_details.get('cumulative_threshold', 0)
|
cumulative_threshold = tax_details.get('cumulative_threshold', 0)
|
||||||
|
|
||||||
if ((threshold and inv.net_total >= threshold) or (cumulative_threshold and supp_credit_amt >= cumulative_threshold)):
|
if ((threshold and inv.net_total >= threshold) or (cumulative_threshold and supp_credit_amt >= cumulative_threshold)):
|
||||||
|
if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(tax_details.tax_on_excess_amount):
|
||||||
|
# Get net total again as TDS is calculated on net total
|
||||||
|
# Grand is used to just check for threshold breach
|
||||||
|
net_total = frappe.db.get_value('Purchase Invoice', invoice_filters, 'sum(net_total)') or 0.0
|
||||||
|
net_total += inv.net_total
|
||||||
|
supp_credit_amt = net_total - cumulative_threshold
|
||||||
|
|
||||||
if ldc and is_valid_certificate(
|
if ldc and is_valid_certificate(
|
||||||
ldc.valid_from, ldc.valid_upto,
|
ldc.valid_from, ldc.valid_upto,
|
||||||
inv.get('posting_date') or inv.get('transaction_date'), tax_deducted,
|
inv.get('posting_date') or inv.get('transaction_date'), tax_deducted,
|
||||||
@@ -264,6 +283,9 @@ def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_dedu
|
|||||||
else:
|
else:
|
||||||
tds_amount = supp_credit_amt * tax_details.rate / 100 if supp_credit_amt > 0 else 0
|
tds_amount = supp_credit_amt * tax_details.rate / 100 if supp_credit_amt > 0 else 0
|
||||||
|
|
||||||
|
if cint(tax_details.round_off_tax_amount):
|
||||||
|
tds_amount = round(tds_amount)
|
||||||
|
|
||||||
return tds_amount
|
return tds_amount
|
||||||
|
|
||||||
def get_tcs_amount(parties, inv, tax_details, fiscal_year_details, vouchers, adv_vouchers):
|
def get_tcs_amount(parties, inv, tax_details, fiscal_year_details, vouchers, adv_vouchers):
|
||||||
|
|||||||
@@ -87,6 +87,31 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
|||||||
for d in invoices:
|
for d in invoices:
|
||||||
d.cancel()
|
d.cancel()
|
||||||
|
|
||||||
|
def test_tax_withholding_category_checks(self):
|
||||||
|
invoices = []
|
||||||
|
frappe.db.set_value("Supplier", "Test TDS Supplier3", "tax_withholding_category", "New TDS Category")
|
||||||
|
|
||||||
|
# First Invoice with no tds check
|
||||||
|
pi = create_purchase_invoice(supplier = "Test TDS Supplier3", rate = 20000, do_not_save=True)
|
||||||
|
pi.apply_tds = 0
|
||||||
|
pi.save()
|
||||||
|
pi.submit()
|
||||||
|
invoices.append(pi)
|
||||||
|
|
||||||
|
# Second Invoice will apply TDS checked
|
||||||
|
pi1 = create_purchase_invoice(supplier = "Test TDS Supplier3", rate = 20000)
|
||||||
|
pi1.submit()
|
||||||
|
invoices.append(pi1)
|
||||||
|
|
||||||
|
# Cumulative threshold is 30000
|
||||||
|
# Threshold calculation should be on both the invoices
|
||||||
|
# TDS should be applied only on 1000
|
||||||
|
self.assertEqual(pi1.taxes[0].tax_amount, 1000)
|
||||||
|
|
||||||
|
for d in invoices:
|
||||||
|
d.cancel()
|
||||||
|
|
||||||
|
|
||||||
def test_cumulative_threshold_tcs(self):
|
def test_cumulative_threshold_tcs(self):
|
||||||
frappe.db.set_value("Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS")
|
frappe.db.set_value("Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS")
|
||||||
invoices = []
|
invoices = []
|
||||||
@@ -195,7 +220,7 @@ 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']:
|
for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2', 'Test TDS Supplier3']:
|
||||||
if frappe.db.exists('Supplier', name):
|
if frappe.db.exists('Supplier', name):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -311,3 +336,23 @@ def create_tax_with_holding_category():
|
|||||||
'account': 'TDS - _TC'
|
'account': 'TDS - _TC'
|
||||||
}]
|
}]
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
|
if not frappe.db.exists("Tax Withholding Category", "New TDS Category"):
|
||||||
|
frappe.get_doc({
|
||||||
|
"doctype": "Tax Withholding Category",
|
||||||
|
"name": "New TDS Category",
|
||||||
|
"category_name": "New TDS Category",
|
||||||
|
"round_off_tax_amount": 1,
|
||||||
|
"consider_party_ledger_amount": 1,
|
||||||
|
"tax_on_excess_amount": 1,
|
||||||
|
"rates": [{
|
||||||
|
'fiscal_year': fiscal_year,
|
||||||
|
'tax_withholding_rate': 10,
|
||||||
|
'single_threshold': 0,
|
||||||
|
'cumulative_threshold': 30000
|
||||||
|
}],
|
||||||
|
"accounts": [{
|
||||||
|
'company': '_Test Company',
|
||||||
|
'account': 'TDS - _TC'
|
||||||
|
}]
|
||||||
|
}).insert()
|
||||||
|
|||||||
@@ -241,6 +241,7 @@ class GrossProfitGenerator(object):
|
|||||||
sle.voucher_detail_no == row.item_row:
|
sle.voucher_detail_no == row.item_row:
|
||||||
previous_stock_value = len(my_sle) > i+1 and \
|
previous_stock_value = len(my_sle) > i+1 and \
|
||||||
flt(my_sle[i+1].stock_value) or 0.0
|
flt(my_sle[i+1].stock_value) or 0.0
|
||||||
|
|
||||||
if previous_stock_value:
|
if previous_stock_value:
|
||||||
return (previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
|
return (previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
|
||||||
else:
|
else:
|
||||||
@@ -335,7 +336,7 @@ class GrossProfitGenerator(object):
|
|||||||
res = frappe.db.sql("""select item_code, voucher_type, voucher_no,
|
res = frappe.db.sql("""select item_code, voucher_type, voucher_no,
|
||||||
voucher_detail_no, stock_value, warehouse, actual_qty as qty
|
voucher_detail_no, stock_value, warehouse, actual_qty as qty
|
||||||
from `tabStock Ledger Entry`
|
from `tabStock Ledger Entry`
|
||||||
where company=%(company)s
|
where company=%(company)s and is_cancelled = 0
|
||||||
order by
|
order by
|
||||||
item_code desc, warehouse desc, posting_date desc,
|
item_code desc, warehouse desc, posting_date desc,
|
||||||
posting_time desc, creation desc""", self.filters, as_dict=True)
|
posting_time desc, creation desc""", self.filters, as_dict=True)
|
||||||
|
|||||||
39
erpnext/change_log/v13/v13_8_0.md
Normal file
39
erpnext/change_log/v13/v13_8_0.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Version 13.8.0 Release Notes
|
||||||
|
|
||||||
|
### Features & Enhancements
|
||||||
|
- Report to show COGS by item groups ([#26222](https://github.com/frappe/erpnext/pull/26222))
|
||||||
|
- Enhancements in TDS ([#26677](https://github.com/frappe/erpnext/pull/26677))
|
||||||
|
- API Endpoint to update halted Razorpay subscriptions ([#26564](https://github.com/frappe/erpnext/pull/26564))
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Incorrect bom name ([#26600](https://github.com/frappe/erpnext/pull/26600))
|
||||||
|
- Exchange rate revaluation posting date and precision fixes ([#26651](https://github.com/frappe/erpnext/pull/26651))
|
||||||
|
- POS item cart dom updates ([#26460](https://github.com/frappe/erpnext/pull/26460))
|
||||||
|
- General Ledger report not working with filter group by ([#26439](https://github.com/frappe/erpnext/pull/26438))
|
||||||
|
- Tax calculation for Recurring additional salary ([#24206](https://github.com/frappe/erpnext/pull/24206))
|
||||||
|
- Validation check for batch for stock reconciliation type in stock entry ([#26487](https://github.com/frappe/erpnext/pull/26487))
|
||||||
|
- Improved UX for additional discount field ([#26502](https://github.com/frappe/erpnext/pull/26502))
|
||||||
|
- Add missing cess amount in GSTR-3B report ([#26644](https://github.com/frappe/erpnext/pull/26644))
|
||||||
|
- Optimized code for reposting item valuation ([#26431](https://github.com/frappe/erpnext/pull/26431))
|
||||||
|
- FG item not fetched in manufacture entry ([#26508](https://github.com/frappe/erpnext/pull/26508))
|
||||||
|
- Errors on parallel requests creation of company for India ([#26420](https://github.com/frappe/erpnext/pull/26420))
|
||||||
|
- Incorrect valuation rate calculation in gross profit report ([#26558](https://github.com/frappe/erpnext/pull/26558))
|
||||||
|
- Empty "against account" in Purchase Receipt GLE ([#26712](https://github.com/frappe/erpnext/pull/26712))
|
||||||
|
- Remove cancelled entries from Stock and Account Value comparison report ([#26721](https://github.com/frappe/erpnext/pull/26721))
|
||||||
|
- Remove manual permission checking ([#26691](https://github.com/frappe/erpnext/pull/26691))
|
||||||
|
- Delete child docs when parent doc is deleted ([#26518](https://github.com/frappe/erpnext/pull/26518))
|
||||||
|
- GST Reports timeout issue ([#26646](https://github.com/frappe/erpnext/pull/26646))
|
||||||
|
- Parent condition in pricing rules ([#26727](https://github.com/frappe/erpnext/pull/26727))
|
||||||
|
- Added Company filters for Loan ([#26294](https://github.com/frappe/erpnext/pull/26294))
|
||||||
|
- Incorrect discount amount on amended document ([#26292](https://github.com/frappe/erpnext/pull/26292))
|
||||||
|
- Exchange gain loss not set for advances linked with invoices ([#26436](https://github.com/frappe/erpnext/pull/26436))
|
||||||
|
- Unallocated amount in Payment Entry after taxes ([#26412](https://github.com/frappe/erpnext/pull/26412))
|
||||||
|
- Wrong operation time in Work Order ([#26613](https://github.com/frappe/erpnext/pull/26613))
|
||||||
|
- Serial No and Batch validation ([#26614](https://github.com/frappe/erpnext/pull/26614))
|
||||||
|
- Gl Entries for exchange gain loss ([#26734](https://github.com/frappe/erpnext/pull/26734))
|
||||||
|
- TDS computation summary shows cancelled invoices ([#26485](https://github.com/frappe/erpnext/pull/26485))
|
||||||
|
- Price List rate not fetched for return sales invoice fixed ([#26560](https://github.com/frappe/erpnext/pull/26560))
|
||||||
|
- Included company in link document type filters for contact ([#26576](https://github.com/frappe/erpnext/pull/26576))
|
||||||
|
- Ignore mandatory fields while creating payment reconciliation Journal Entry ([#26643](https://github.com/frappe/erpnext/pull/26643))
|
||||||
|
- Unable to download GSTR-1 json ([#26418](https://github.com/frappe/erpnext/pull/26418))
|
||||||
|
- Paging buttons not working on item group portal page ([#26498](https://github.com/frappe/erpnext/pull/26498))
|
||||||
@@ -674,17 +674,22 @@ class AccountsController(TransactionBase):
|
|||||||
if self.get('doctype') in ['Purchase Invoice', 'Sales Invoice']:
|
if self.get('doctype') in ['Purchase Invoice', 'Sales Invoice']:
|
||||||
for d in self.get("advances"):
|
for d in self.get("advances"):
|
||||||
if d.exchange_gain_loss:
|
if d.exchange_gain_loss:
|
||||||
party = self.supplier if self.get('doctype') == 'Purchase Invoice' else self.customer
|
is_purchase_invoice = self.get('doctype') == 'Purchase Invoice'
|
||||||
party_account = self.credit_to if self.get('doctype') == 'Purchase Invoice' else self.debit_to
|
party = self.supplier if is_purchase_invoice else self.customer
|
||||||
party_type = "Supplier" if self.get('doctype') == 'Purchase Invoice' else "Customer"
|
party_account = self.credit_to if is_purchase_invoice else self.debit_to
|
||||||
|
party_type = "Supplier" if is_purchase_invoice else "Customer"
|
||||||
|
|
||||||
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:
|
||||||
|
frappe.throw(_("Please set Default Exchange Gain/Loss Account in 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:
|
||||||
frappe.throw(_("Currency for {0} must be {1}").format(d.account, self.company_currency))
|
frappe.throw(_("Currency for {0} must be {1}").format(gain_loss_account, self.company_currency))
|
||||||
|
|
||||||
# for purchase
|
# for purchase
|
||||||
dr_or_cr = 'debit' if d.exchange_gain_loss > 0 else 'credit'
|
dr_or_cr = 'debit' if d.exchange_gain_loss > 0 else 'credit'
|
||||||
|
if not is_purchase_invoice:
|
||||||
# just reverse for sales?
|
# just reverse for sales?
|
||||||
dr_or_cr = 'debit' if dr_or_cr == 'credit' else 'credit'
|
dr_or_cr = 'debit' if dr_or_cr == 'credit' else 'credit'
|
||||||
|
|
||||||
|
|||||||
@@ -407,6 +407,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
INNER JOIN `tabBatch` batch on sle.batch_no = batch.name
|
INNER JOIN `tabBatch` batch on sle.batch_no = batch.name
|
||||||
where
|
where
|
||||||
batch.disabled = 0
|
batch.disabled = 0
|
||||||
|
and sle.is_cancelled = 0
|
||||||
and sle.item_code = %(item_code)s
|
and sle.item_code = %(item_code)s
|
||||||
and sle.warehouse = %(warehouse)s
|
and sle.warehouse = %(warehouse)s
|
||||||
and (sle.batch_no like %(txt)s
|
and (sle.batch_no like %(txt)s
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ frappe.ui.form.on('Loan Application', {
|
|||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
frm.trigger("toggle_fields");
|
frm.trigger("toggle_fields");
|
||||||
frm.trigger("add_toolbar_buttons");
|
frm.trigger("add_toolbar_buttons");
|
||||||
frm.set_query("loan_type", () => {
|
frm.set_query('loan_type', () => {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
company: frm.doc.company
|
company: frm.doc.company
|
||||||
|
|||||||
@@ -748,7 +748,7 @@ def get_valuation_rate(args):
|
|||||||
if valuation_rate <= 0:
|
if valuation_rate <= 0:
|
||||||
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
||||||
from `tabStock Ledger Entry`
|
from `tabStock Ledger Entry`
|
||||||
where item_code = %s and valuation_rate > 0
|
where item_code = %s and valuation_rate > 0 and is_cancelled = 0
|
||||||
order by posting_date desc, posting_time desc, creation desc limit 1""", args['item_code'])
|
order by posting_date desc, posting_time desc, creation desc limit 1""", args['item_code'])
|
||||||
|
|
||||||
valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0
|
valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0
|
||||||
|
|||||||
@@ -297,3 +297,5 @@ erpnext.patches.v13_0.update_level_in_bom #1234sswef
|
|||||||
erpnext.patches.v13_0.add_missing_fg_item_for_stock_entry
|
erpnext.patches.v13_0.add_missing_fg_item_for_stock_entry
|
||||||
erpnext.patches.v13_0.update_subscription_status_in_memberships
|
erpnext.patches.v13_0.update_subscription_status_in_memberships
|
||||||
erpnext.patches.v13_0.update_amt_in_work_order_required_items
|
erpnext.patches.v13_0.update_amt_in_work_order_required_items
|
||||||
|
erpnext.patches.v13_0.update_export_type_for_gst
|
||||||
|
erpnext.patches.v13_0.update_tds_check_field #3
|
||||||
|
|||||||
24
erpnext/patches/v13_0/update_export_type_for_gst.py
Normal file
24
erpnext/patches/v13_0/update_export_type_for_gst.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||||
|
if not company:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Update custom fields
|
||||||
|
fieldname = frappe.db.get_value('Custom Field', {'dt': 'Customer', 'fieldname': 'export_type'})
|
||||||
|
if fieldname:
|
||||||
|
frappe.db.set_value('Custom Field', fieldname, 'default', '')
|
||||||
|
|
||||||
|
fieldname = frappe.db.get_value('Custom Field', {'dt': 'Supplier', 'fieldname': 'export_type'})
|
||||||
|
if fieldname:
|
||||||
|
frappe.db.set_value('Custom Field', fieldname, 'default', '')
|
||||||
|
|
||||||
|
# Update Customer/Supplier Masters
|
||||||
|
frappe.db.sql("""
|
||||||
|
UPDATE `tabCustomer` set export_type = '' WHERE gst_category NOT IN ('SEZ', 'Overseas', 'Deemed Export')
|
||||||
|
""")
|
||||||
|
|
||||||
|
frappe.db.sql("""
|
||||||
|
UPDATE `tabSupplier` set export_type = '' WHERE gst_category NOT IN ('SEZ', 'Overseas')
|
||||||
|
""")
|
||||||
9
erpnext/patches/v13_0/update_tds_check_field.py
Normal file
9
erpnext/patches/v13_0/update_tds_check_field.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
if frappe.db.has_table("Tax Withholding Category") \
|
||||||
|
and frappe.db.has_column("Tax Withholding Category", "round_off_tax_amount"):
|
||||||
|
frappe.db.sql("""
|
||||||
|
UPDATE `tabTax Withholding Category` set round_off_tax_amount = 0
|
||||||
|
WHERE round_off_tax_amount IS NULL
|
||||||
|
""")
|
||||||
@@ -112,11 +112,11 @@ class AdditionalSalary(Document):
|
|||||||
no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1
|
no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1
|
||||||
return amount_per_day * no_of_days
|
return amount_per_day * no_of_days
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_additional_salaries(employee, start_date, end_date, component_type):
|
def get_additional_salaries(employee, start_date, end_date, component_type):
|
||||||
additional_salary_list = frappe.db.sql("""
|
additional_salary_list = frappe.db.sql("""
|
||||||
select name, salary_component as component, type, amount,
|
select name, salary_component as component, type, amount, overwrite_salary_structure_amount as overwrite,
|
||||||
overwrite_salary_structure_amount as overwrite,
|
deduct_full_tax_on_selected_payroll_date, is_recurring
|
||||||
deduct_full_tax_on_selected_payroll_date
|
|
||||||
from `tabAdditional Salary`
|
from `tabAdditional Salary`
|
||||||
where employee=%(employee)s
|
where employee=%(employee)s
|
||||||
and docstatus = 1
|
and docstatus = 1
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"year_to_date",
|
"year_to_date",
|
||||||
"section_break_5",
|
"section_break_5",
|
||||||
"additional_salary",
|
"additional_salary",
|
||||||
|
"is_recurring_additional_salary",
|
||||||
"statistical_component",
|
"statistical_component",
|
||||||
"depends_on_payment_days",
|
"depends_on_payment_days",
|
||||||
"exempted_from_income_tax",
|
"exempted_from_income_tax",
|
||||||
@@ -235,11 +236,19 @@
|
|||||||
"label": "Year To Date",
|
"label": "Year To Date",
|
||||||
"options": "currency",
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='earnings' && doc.additional_salary",
|
||||||
|
"fieldname": "is_recurring_additional_salary",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Is Recurring Additional Salary",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-01-14 13:39:15.847158",
|
"modified": "2021-03-14 13:39:15.847158",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Salary Detail",
|
"name": "Salary Detail",
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import datetime, math
|
|||||||
|
|
||||||
from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words, formatdate, get_first_day
|
from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words, formatdate, get_first_day
|
||||||
from frappe.model.naming import make_autoname
|
from frappe.model.naming import make_autoname
|
||||||
|
from frappe.utils.background_jobs import enqueue
|
||||||
|
|
||||||
from frappe import msgprint, _
|
from frappe import msgprint, _
|
||||||
from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates
|
from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates
|
||||||
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
||||||
from erpnext.utilities.transaction_base import TransactionBase
|
from erpnext.utilities.transaction_base import TransactionBase
|
||||||
from frappe.utils.background_jobs import enqueue
|
|
||||||
from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salaries
|
from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salaries
|
||||||
from erpnext.payroll.doctype.payroll_period.payroll_period import get_period_factor, get_payroll_period
|
from erpnext.payroll.doctype.payroll_period.payroll_period import get_period_factor, get_payroll_period
|
||||||
from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount
|
from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount
|
||||||
@@ -618,7 +618,8 @@ class SalarySlip(TransactionBase):
|
|||||||
get_salary_component_data(additional_salary.component),
|
get_salary_component_data(additional_salary.component),
|
||||||
additional_salary.amount,
|
additional_salary.amount,
|
||||||
component_type,
|
component_type,
|
||||||
additional_salary
|
additional_salary,
|
||||||
|
is_recurring = additional_salary.is_recurring
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_tax_components(self, payroll_period):
|
def add_tax_components(self, payroll_period):
|
||||||
@@ -639,7 +640,7 @@ class SalarySlip(TransactionBase):
|
|||||||
tax_row = get_salary_component_data(d)
|
tax_row = get_salary_component_data(d)
|
||||||
self.update_component_row(tax_row, tax_amount, "deductions")
|
self.update_component_row(tax_row, tax_amount, "deductions")
|
||||||
|
|
||||||
def update_component_row(self, component_data, amount, component_type, additional_salary=None):
|
def update_component_row(self, component_data, amount, component_type, additional_salary=None, is_recurring = 0):
|
||||||
component_row = None
|
component_row = None
|
||||||
for d in self.get(component_type):
|
for d in self.get(component_type):
|
||||||
if d.salary_component != component_data.salary_component:
|
if d.salary_component != component_data.salary_component:
|
||||||
@@ -677,6 +678,7 @@ class SalarySlip(TransactionBase):
|
|||||||
component_row.set(attr, component_data.get(attr))
|
component_row.set(attr, component_data.get(attr))
|
||||||
|
|
||||||
if additional_salary:
|
if additional_salary:
|
||||||
|
component_row.is_recurring_additional_salary = is_recurring
|
||||||
component_row.default_amount = 0
|
component_row.default_amount = 0
|
||||||
component_row.additional_amount = amount
|
component_row.additional_amount = amount
|
||||||
component_row.additional_salary = additional_salary.name
|
component_row.additional_salary = additional_salary.name
|
||||||
@@ -710,6 +712,7 @@ class SalarySlip(TransactionBase):
|
|||||||
# get remaining numbers of sub-period (period for which one salary is processed)
|
# get remaining numbers of sub-period (period for which one salary is processed)
|
||||||
remaining_sub_periods = get_period_factor(self.employee,
|
remaining_sub_periods = get_period_factor(self.employee,
|
||||||
self.start_date, self.end_date, self.payroll_frequency, payroll_period)[1]
|
self.start_date, self.end_date, self.payroll_frequency, payroll_period)[1]
|
||||||
|
|
||||||
# get taxable_earnings, paid_taxes for previous period
|
# get taxable_earnings, paid_taxes for previous period
|
||||||
previous_taxable_earnings = self.get_taxable_earnings_for_prev_period(payroll_period.start_date,
|
previous_taxable_earnings = self.get_taxable_earnings_for_prev_period(payroll_period.start_date,
|
||||||
self.start_date, tax_slab.allow_tax_exemption)
|
self.start_date, tax_slab.allow_tax_exemption)
|
||||||
@@ -869,8 +872,16 @@ class SalarySlip(TransactionBase):
|
|||||||
|
|
||||||
if earning.is_tax_applicable:
|
if earning.is_tax_applicable:
|
||||||
if additional_amount:
|
if additional_amount:
|
||||||
|
if not earning.is_recurring_additional_salary:
|
||||||
taxable_earnings += (amount - additional_amount)
|
taxable_earnings += (amount - additional_amount)
|
||||||
additional_income += additional_amount
|
additional_income += additional_amount
|
||||||
|
else:
|
||||||
|
to_date = frappe.db.get_value("Additional Salary", earning.additional_salary, 'to_date')
|
||||||
|
period = (getdate(to_date).month - getdate(self.start_date).month) + 1
|
||||||
|
if period > 0:
|
||||||
|
taxable_earnings += (amount - additional_amount) * period
|
||||||
|
additional_income += additional_amount * period
|
||||||
|
|
||||||
if earning.deduct_full_tax_on_selected_payroll_date:
|
if earning.deduct_full_tax_on_selected_payroll_date:
|
||||||
additional_income_with_full_tax += additional_amount
|
additional_income_with_full_tax += additional_amount
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None,
|
|||||||
"earnings": make_earning_salary_component(setup=True, test_tax=test_tax, company_list=["_Test Company"]),
|
"earnings": make_earning_salary_component(setup=True, test_tax=test_tax, company_list=["_Test Company"]),
|
||||||
"deductions": make_deduction_salary_component(setup=True, test_tax=test_tax, company_list=["_Test Company"]),
|
"deductions": make_deduction_salary_component(setup=True, test_tax=test_tax, company_list=["_Test Company"]),
|
||||||
"payroll_frequency": payroll_frequency,
|
"payroll_frequency": payroll_frequency,
|
||||||
"payment_account": get_random("Account", filters={"account_currency": currency}),
|
"payment_account": get_random("Account", filters={'account_currency': currency}),
|
||||||
"currency": currency
|
"currency": currency
|
||||||
}
|
}
|
||||||
if other_details and isinstance(other_details, dict):
|
if other_details and isinstance(other_details, dict):
|
||||||
|
|||||||
@@ -280,9 +280,15 @@ class GSTR3BReport(Document):
|
|||||||
if self.get('invoice_items'):
|
if self.get('invoice_items'):
|
||||||
# Build itemised tax for export invoices, nil and exempted where tax table is blank
|
# Build itemised tax for export invoices, nil and exempted where tax table is blank
|
||||||
for invoice, items in iteritems(self.invoice_items):
|
for invoice, items in iteritems(self.invoice_items):
|
||||||
if invoice not in self.items_based_on_tax_rate and (self.invoice_detail_map.get(invoice, {}).get('export_type')
|
if invoice not in self.items_based_on_tax_rate and self.invoice_detail_map.get(invoice, {}).get('export_type') \
|
||||||
== "Without Payment of Tax"):
|
== "Without Payment of Tax" and self.invoice_detail_map.get(invoice, {}).get('gst_category') == "Overseas":
|
||||||
self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys())
|
self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys())
|
||||||
|
else:
|
||||||
|
for item in items.keys():
|
||||||
|
if item in self.is_nil_exempt + self.is_non_gst and \
|
||||||
|
item not in self.items_based_on_tax_rate.get(invoice, {}).get(0, []):
|
||||||
|
self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, [])
|
||||||
|
self.items_based_on_tax_rate[invoice][0].append(item)
|
||||||
|
|
||||||
def set_outward_taxable_supplies(self):
|
def set_outward_taxable_supplies(self):
|
||||||
inter_state_supply_details = {}
|
inter_state_supply_details = {}
|
||||||
|
|||||||
@@ -641,7 +641,6 @@ def make_custom_fields(update=True):
|
|||||||
'label': 'Export Type',
|
'label': 'Export Type',
|
||||||
'fieldtype': 'Select',
|
'fieldtype': 'Select',
|
||||||
'insert_after': 'gst_category',
|
'insert_after': 'gst_category',
|
||||||
'default': 'Without Payment of Tax',
|
|
||||||
'depends_on':'eval:in_list(["SEZ", "Overseas"], doc.gst_category)',
|
'depends_on':'eval:in_list(["SEZ", "Overseas"], doc.gst_category)',
|
||||||
'options': '\nWith Payment of Tax\nWithout Payment of Tax'
|
'options': '\nWith Payment of Tax\nWithout Payment of Tax'
|
||||||
}
|
}
|
||||||
@@ -660,7 +659,6 @@ def make_custom_fields(update=True):
|
|||||||
'label': 'Export Type',
|
'label': 'Export Type',
|
||||||
'fieldtype': 'Select',
|
'fieldtype': 'Select',
|
||||||
'insert_after': 'gst_category',
|
'insert_after': 'gst_category',
|
||||||
'default': 'Without Payment of Tax',
|
|
||||||
'depends_on':'eval:in_list(["SEZ", "Overseas", "Deemed Export"], doc.gst_category)',
|
'depends_on':'eval:in_list(["SEZ", "Overseas", "Deemed Export"], doc.gst_category)',
|
||||||
'options': '\nWith Payment of Tax\nWithout Payment of Tax'
|
'options': '\nWith Payment of Tax\nWithout Payment of Tax'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -286,7 +286,8 @@ class Gstr1Report(object):
|
|||||||
# Build itemised tax for export invoices where tax table is blank
|
# Build itemised tax for export invoices where tax table is blank
|
||||||
for invoice, items in iteritems(self.invoice_items):
|
for invoice, items in iteritems(self.invoice_items):
|
||||||
if invoice not in self.items_based_on_tax_rate and invoice not in unidentified_gst_accounts_invoice \
|
if invoice not in self.items_based_on_tax_rate and invoice not in unidentified_gst_accounts_invoice \
|
||||||
and frappe.db.get_value(self.doctype, invoice, "export_type") == "Without Payment of Tax":
|
and self.invoices.get(invoice, {}).get('export_type') == "Without Payment of Tax" \
|
||||||
|
and self.invoices.get(invoice, {}).get('gst_category') == "Overseas":
|
||||||
self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys())
|
self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys())
|
||||||
|
|
||||||
def get_columns(self):
|
def get_columns(self):
|
||||||
|
|||||||
@@ -162,19 +162,19 @@ def get_batch_qty(batch_no=None, warehouse=None, item_code=None, posting_date=No
|
|||||||
|
|
||||||
out = float(frappe.db.sql("""select sum(actual_qty)
|
out = float(frappe.db.sql("""select sum(actual_qty)
|
||||||
from `tabStock Ledger Entry`
|
from `tabStock Ledger Entry`
|
||||||
where warehouse=%s and batch_no=%s {0}""".format(cond),
|
where is_cancelled = 0 and warehouse=%s and batch_no=%s {0}""".format(cond),
|
||||||
(warehouse, batch_no))[0][0] or 0)
|
(warehouse, batch_no))[0][0] or 0)
|
||||||
|
|
||||||
if batch_no and not warehouse:
|
if batch_no and not warehouse:
|
||||||
out = frappe.db.sql('''select warehouse, sum(actual_qty) as qty
|
out = frappe.db.sql('''select warehouse, sum(actual_qty) as qty
|
||||||
from `tabStock Ledger Entry`
|
from `tabStock Ledger Entry`
|
||||||
where batch_no=%s
|
where is_cancelled = 0 and batch_no=%s
|
||||||
group by warehouse''', batch_no, as_dict=1)
|
group by warehouse''', batch_no, as_dict=1)
|
||||||
|
|
||||||
if not batch_no and item_code and warehouse:
|
if not batch_no and item_code and warehouse:
|
||||||
out = frappe.db.sql('''select batch_no, sum(actual_qty) as qty
|
out = frappe.db.sql('''select batch_no, sum(actual_qty) as qty
|
||||||
from `tabStock Ledger Entry`
|
from `tabStock Ledger Entry`
|
||||||
where item_code = %s and warehouse=%s
|
where is_cancelled = 0 and item_code = %s and warehouse=%s
|
||||||
group by batch_no''', (item_code, warehouse), as_dict=1)
|
group by batch_no''', (item_code, warehouse), as_dict=1)
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|||||||
@@ -239,6 +239,7 @@ def get_available_item_locations_for_batched_item(item_code, from_warehouses, re
|
|||||||
and sle.`item_code`=%(item_code)s
|
and sle.`item_code`=%(item_code)s
|
||||||
and sle.`company` = %(company)s
|
and sle.`company` = %(company)s
|
||||||
and batch.disabled = 0
|
and batch.disabled = 0
|
||||||
|
and sle.is_cancelled=0
|
||||||
and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s
|
and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s
|
||||||
{warehouse_condition}
|
{warehouse_condition}
|
||||||
GROUP BY
|
GROUP BY
|
||||||
|
|||||||
@@ -1789,7 +1789,7 @@ def get_expired_batch_items():
|
|||||||
from `tabBatch` b, `tabStock Ledger Entry` sle
|
from `tabBatch` b, `tabStock Ledger Entry` sle
|
||||||
where b.expiry_date <= %s
|
where b.expiry_date <= %s
|
||||||
and b.expiry_date is not NULL
|
and b.expiry_date is not NULL
|
||||||
and b.batch_id = sle.batch_no
|
and b.batch_id = sle.batch_no and sle.is_cancelled = 0
|
||||||
group by sle.warehouse, sle.item_code, sle.batch_no""",(nowdate()), as_dict=1)
|
group by sle.warehouse, sle.item_code, sle.batch_no""",(nowdate()), as_dict=1)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class StockLedgerEntry(Document):
|
|||||||
if self.batch_no and not self.get("allow_negative_stock"):
|
if self.batch_no and not self.get("allow_negative_stock"):
|
||||||
batch_bal_after_transaction = flt(frappe.db.sql("""select sum(actual_qty)
|
batch_bal_after_transaction = flt(frappe.db.sql("""select sum(actual_qty)
|
||||||
from `tabStock Ledger Entry`
|
from `tabStock Ledger Entry`
|
||||||
where warehouse=%s and item_code=%s and batch_no=%s""",
|
where is_cancelled =0 and warehouse=%s and item_code=%s and batch_no=%s""",
|
||||||
(self.warehouse, self.item_code, self.batch_no))[0][0])
|
(self.warehouse, self.item_code, self.batch_no))[0][0])
|
||||||
|
|
||||||
if batch_bal_after_transaction < 0:
|
if batch_bal_after_transaction < 0:
|
||||||
@@ -152,7 +152,7 @@ class StockLedgerEntry(Document):
|
|||||||
last_transaction_time = frappe.db.sql("""
|
last_transaction_time = frappe.db.sql("""
|
||||||
select MAX(timestamp(posting_date, posting_time)) as posting_time
|
select MAX(timestamp(posting_date, posting_time)) as posting_time
|
||||||
from `tabStock Ledger Entry`
|
from `tabStock Ledger Entry`
|
||||||
where docstatus = 1 and item_code = %s
|
where docstatus = 1 and is_cancelled = 0 and item_code = %s
|
||||||
and warehouse = %s""", (self.item_code, self.warehouse))[0][0]
|
and warehouse = %s""", (self.item_code, self.warehouse))[0][0]
|
||||||
|
|
||||||
cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")
|
cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")
|
||||||
|
|||||||
0
erpnext/stock/report/cogs_by_item_group/__init__.py
Normal file
0
erpnext/stock/report/cogs_by_item_group/__init__.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
|
||||||
|
frappe.query_reports["COGS By Item Group"] = {
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
label: __("Company"),
|
||||||
|
fieldname: "company",
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Company",
|
||||||
|
mandatory: true,
|
||||||
|
default: frappe.defaults.get_user_default("Company"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __("From Date"),
|
||||||
|
fieldname: "from_date",
|
||||||
|
fieldtype: "Date",
|
||||||
|
mandatory: true,
|
||||||
|
default: frappe.datetime.year_start(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __("To Date"),
|
||||||
|
fieldname: "to_date",
|
||||||
|
fieldtype: "Date",
|
||||||
|
mandatory: true,
|
||||||
|
default: frappe.datetime.get_today(),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"add_total_row": 0,
|
||||||
|
"columns": [],
|
||||||
|
"creation": "2021-06-02 18:59:19.830928",
|
||||||
|
"disable_prepared_report": 0,
|
||||||
|
"disabled": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Report",
|
||||||
|
"filters": [],
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": "Yes",
|
||||||
|
"modified": "2021-06-02 18:59:55.470621",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Stock",
|
||||||
|
"name": "COGS By Item Group",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"prepared_report": 0,
|
||||||
|
"ref_doctype": "GL Entry",
|
||||||
|
"report_name": "COGS By Item Group",
|
||||||
|
"report_type": "Script Report",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"role": "Accounts User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Accounts Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Auditor"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
188
erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py
Normal file
188
erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
|
import datetime
|
||||||
|
from typing import Dict, List, Tuple, Union
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import date_diff
|
||||||
|
|
||||||
|
from erpnext.accounts.report.general_ledger.general_ledger import get_gl_entries
|
||||||
|
|
||||||
|
|
||||||
|
Filters = frappe._dict
|
||||||
|
Row = frappe._dict
|
||||||
|
Data = List[Row]
|
||||||
|
Columns = List[Dict[str, str]]
|
||||||
|
DateTime = Union[datetime.date, datetime.datetime]
|
||||||
|
FilteredEntries = List[Dict[str, Union[str, float, DateTime, None]]]
|
||||||
|
ItemGroupsDict = Dict[Tuple[int, int], Dict[str, Union[str, int]]]
|
||||||
|
SVDList = List[frappe._dict]
|
||||||
|
|
||||||
|
|
||||||
|
def execute(filters: Filters) -> Tuple[Columns, Data]:
|
||||||
|
update_filters_with_account(filters)
|
||||||
|
validate_filters(filters)
|
||||||
|
columns = get_columns()
|
||||||
|
data = get_data(filters)
|
||||||
|
return columns, data
|
||||||
|
|
||||||
|
|
||||||
|
def update_filters_with_account(filters: Filters) -> None:
|
||||||
|
account = frappe.get_value("Company", filters.get("company"), "default_expense_account")
|
||||||
|
filters.update(dict(account=account))
|
||||||
|
|
||||||
|
|
||||||
|
def validate_filters(filters: Filters) -> None:
|
||||||
|
if filters.from_date > filters.to_date:
|
||||||
|
frappe.throw(_("From Date must be before To Date"))
|
||||||
|
|
||||||
|
|
||||||
|
def get_columns() -> Columns:
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'label': 'Item Group',
|
||||||
|
'fieldname': 'item_group',
|
||||||
|
'fieldtype': 'Data',
|
||||||
|
'width': '200'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'COGS Debit',
|
||||||
|
'fieldname': 'cogs_debit',
|
||||||
|
'fieldtype': 'Currency',
|
||||||
|
'width': '200'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_data(filters: Filters) -> Data:
|
||||||
|
filtered_entries = get_filtered_entries(filters)
|
||||||
|
svd_list = get_stock_value_difference_list(filtered_entries)
|
||||||
|
leveled_dict = get_leveled_dict()
|
||||||
|
|
||||||
|
assign_self_values(leveled_dict, svd_list)
|
||||||
|
assign_agg_values(leveled_dict)
|
||||||
|
|
||||||
|
data = []
|
||||||
|
for item in leveled_dict.items():
|
||||||
|
i = item[1]
|
||||||
|
if i['agg_value'] == 0:
|
||||||
|
continue
|
||||||
|
data.append(get_row(i['name'], i['agg_value'], i['is_group'], i['level']))
|
||||||
|
if i['self_value'] < i['agg_value'] and i['self_value'] > 0:
|
||||||
|
data.append(get_row(i['name'], i['self_value'], 0, i['level'] + 1))
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def get_filtered_entries(filters: Filters) -> FilteredEntries:
|
||||||
|
gl_entries = get_gl_entries(filters, [])
|
||||||
|
filtered_entries = []
|
||||||
|
for entry in gl_entries:
|
||||||
|
posting_date = entry.get('posting_date')
|
||||||
|
from_date = filters.get('from_date')
|
||||||
|
if date_diff(from_date, posting_date) > 0:
|
||||||
|
continue
|
||||||
|
filtered_entries.append(entry)
|
||||||
|
return filtered_entries
|
||||||
|
|
||||||
|
|
||||||
|
def get_stock_value_difference_list(filtered_entries: FilteredEntries) -> SVDList:
|
||||||
|
voucher_nos = [fe.get('voucher_no') for fe in filtered_entries]
|
||||||
|
svd_list = frappe.get_list(
|
||||||
|
'Stock Ledger Entry', fields=['item_code','stock_value_difference'],
|
||||||
|
filters=[('voucher_no', 'in', voucher_nos)]
|
||||||
|
)
|
||||||
|
assign_item_groups_to_svd_list(svd_list)
|
||||||
|
return svd_list
|
||||||
|
|
||||||
|
|
||||||
|
def get_leveled_dict() -> OrderedDict:
|
||||||
|
item_groups_dict = get_item_groups_dict()
|
||||||
|
lr_list = sorted(item_groups_dict, key=lambda x : int(x[0]))
|
||||||
|
leveled_dict = OrderedDict()
|
||||||
|
current_level = 0
|
||||||
|
nesting_r = []
|
||||||
|
for l, r in lr_list:
|
||||||
|
while current_level > 0 and nesting_r[-1] < l:
|
||||||
|
nesting_r.pop()
|
||||||
|
current_level -= 1
|
||||||
|
|
||||||
|
leveled_dict[(l,r)] = {
|
||||||
|
'level' : current_level,
|
||||||
|
'name' : item_groups_dict[(l,r)]['name'],
|
||||||
|
'is_group' : item_groups_dict[(l,r)]['is_group']
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(r) - int(l) > 1:
|
||||||
|
current_level += 1
|
||||||
|
nesting_r.append(r)
|
||||||
|
|
||||||
|
update_leveled_dict(leveled_dict)
|
||||||
|
return leveled_dict
|
||||||
|
|
||||||
|
|
||||||
|
def assign_self_values(leveled_dict: OrderedDict, svd_list: SVDList) -> None:
|
||||||
|
key_dict = {v['name']:k for k, v in leveled_dict.items()}
|
||||||
|
for item in svd_list:
|
||||||
|
key = key_dict[item.get("item_group")]
|
||||||
|
leveled_dict[key]['self_value'] += -item.get("stock_value_difference")
|
||||||
|
|
||||||
|
|
||||||
|
def assign_agg_values(leveled_dict: OrderedDict) -> None:
|
||||||
|
keys = list(leveled_dict.keys())[::-1]
|
||||||
|
prev_level = leveled_dict[keys[-1]]['level']
|
||||||
|
accu = [0]
|
||||||
|
for k in keys[:-1]:
|
||||||
|
curr_level = leveled_dict[k]['level']
|
||||||
|
if curr_level == prev_level:
|
||||||
|
accu[-1] += leveled_dict[k]['self_value']
|
||||||
|
leveled_dict[k]['agg_value'] = leveled_dict[k]['self_value']
|
||||||
|
|
||||||
|
elif curr_level > prev_level:
|
||||||
|
accu.append(leveled_dict[k]['self_value'])
|
||||||
|
leveled_dict[k]['agg_value'] = accu[-1]
|
||||||
|
|
||||||
|
elif curr_level < prev_level:
|
||||||
|
accu[-1] += leveled_dict[k]['self_value']
|
||||||
|
leveled_dict[k]['agg_value'] = accu[-1]
|
||||||
|
|
||||||
|
prev_level = curr_level
|
||||||
|
|
||||||
|
# root node
|
||||||
|
rk = keys[-1]
|
||||||
|
leveled_dict[rk]['agg_value'] = sum(accu) + leveled_dict[rk]['self_value']
|
||||||
|
|
||||||
|
|
||||||
|
def get_row(name:str, value:float, is_bold:int, indent:int) -> Row:
|
||||||
|
item_group = name
|
||||||
|
if is_bold:
|
||||||
|
item_group = frappe.bold(item_group)
|
||||||
|
return frappe._dict(item_group=item_group, cogs_debit=value, indent=indent)
|
||||||
|
|
||||||
|
|
||||||
|
def assign_item_groups_to_svd_list(svd_list: SVDList) -> None:
|
||||||
|
ig_map = get_item_groups_map(svd_list)
|
||||||
|
for item in svd_list:
|
||||||
|
item.item_group = ig_map[item.get("item_code")]
|
||||||
|
|
||||||
|
|
||||||
|
def get_item_groups_map(svd_list: SVDList) -> Dict[str, str]:
|
||||||
|
item_codes = set(i['item_code'] for i in svd_list)
|
||||||
|
ig_list = frappe.get_list(
|
||||||
|
'Item', fields=['item_code','item_group'],
|
||||||
|
filters=[('item_code', 'in', item_codes)]
|
||||||
|
)
|
||||||
|
return {i['item_code']:i['item_group'] for i in ig_list}
|
||||||
|
|
||||||
|
|
||||||
|
def get_item_groups_dict() -> ItemGroupsDict:
|
||||||
|
item_groups_list = frappe.get_all("Item Group", fields=("name", "is_group", "lft", "rgt"))
|
||||||
|
return {(i['lft'],i['rgt']):{'name':i['name'], 'is_group':i['is_group']}
|
||||||
|
for i in item_groups_list}
|
||||||
|
|
||||||
|
|
||||||
|
def update_leveled_dict(leveled_dict: OrderedDict) -> None:
|
||||||
|
for k in leveled_dict:
|
||||||
|
leveled_dict[k].update({'self_value':0, 'agg_value':0})
|
||||||
@@ -69,7 +69,7 @@ def get_consumed_details(filters):
|
|||||||
i.stock_uom, sle.actual_qty, sle.stock_value_difference,
|
i.stock_uom, sle.actual_qty, sle.stock_value_difference,
|
||||||
sle.voucher_no, sle.voucher_type
|
sle.voucher_no, sle.voucher_type
|
||||||
from `tabStock Ledger Entry` sle, `tabItem` i
|
from `tabStock Ledger Entry` sle, `tabItem` i
|
||||||
where sle.item_code=i.name and sle.actual_qty < 0 %s""" % conditions, values, as_dict=1):
|
where sle.is_cancelled = 0 and sle.item_code=i.name and sle.actual_qty < 0 %s""" % conditions, values, as_dict=1):
|
||||||
consumed_details.setdefault(d.item_code, []).append(d)
|
consumed_details.setdefault(d.item_code, []).append(d)
|
||||||
|
|
||||||
return consumed_details
|
return consumed_details
|
||||||
|
|||||||
Reference in New Issue
Block a user