mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-13 20:05:09 +00:00
Merge branch 'version-14-hotfix' into mergify/bp/version-14-hotfix/pr-32846
This commit is contained in:
@@ -57,6 +57,8 @@
|
||||
"column_break_28",
|
||||
"total",
|
||||
"net_total",
|
||||
"tax_withholding_net_total",
|
||||
"base_tax_withholding_net_total",
|
||||
"taxes_section",
|
||||
"taxes_and_charges",
|
||||
"column_break_58",
|
||||
@@ -89,7 +91,6 @@
|
||||
"section_break_44",
|
||||
"apply_discount_on",
|
||||
"base_discount_amount",
|
||||
"additional_discount_account",
|
||||
"column_break_46",
|
||||
"additional_discount_percentage",
|
||||
"discount_amount",
|
||||
@@ -1421,6 +1422,26 @@
|
||||
"label": "Is Old Subcontracting Flow",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "tax_withholding_net_total",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
"label": "Tax Withholding Net Total",
|
||||
"no_copy": 1,
|
||||
"options": "currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "base_tax_withholding_net_total",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
"label": "Base Tax Withholding Net Total",
|
||||
"no_copy": 1,
|
||||
"options": "currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"collapsible_depends_on": "tax_withheld_vouchers",
|
||||
"fieldname": "tax_withheld_vouchers_section",
|
||||
@@ -1519,7 +1540,7 @@
|
||||
"idx": 204,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-10-11 13:04:44.304389",
|
||||
"modified": "2022-11-04 01:02:44.544878",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"discount_amount",
|
||||
"base_rate_with_margin",
|
||||
"sec_break2",
|
||||
"apply_tds",
|
||||
"rate",
|
||||
"amount",
|
||||
"item_tax_template",
|
||||
@@ -868,6 +869,12 @@
|
||||
"label": "Product Bundle",
|
||||
"options": "Product Bundle",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "apply_tds",
|
||||
"fieldtype": "Check",
|
||||
"label": "Apply TDS"
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
|
||||
@@ -61,6 +61,9 @@ def get_party_details(inv):
|
||||
|
||||
|
||||
def get_party_tax_withholding_details(inv, tax_withholding_category=None):
|
||||
if inv.doctype == "Payment Entry":
|
||||
inv.tax_withholding_net_total = inv.net_total
|
||||
|
||||
pan_no = ""
|
||||
parties = []
|
||||
party_type, party = get_party_details(inv)
|
||||
@@ -242,7 +245,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
|
||||
if party_type == "Supplier":
|
||||
ldc = get_lower_deduction_certificate(tax_details, pan_no)
|
||||
if tax_deducted:
|
||||
net_total = inv.net_total
|
||||
net_total = inv.tax_withholding_net_total
|
||||
if ldc:
|
||||
tax_amount = get_tds_amount_from_ldc(
|
||||
ldc, parties, pan_no, tax_details, posting_date, net_total
|
||||
@@ -272,6 +275,11 @@ 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"):
|
||||
doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice"
|
||||
field = (
|
||||
"base_tax_withholding_net_total as base_net_total"
|
||||
if party_type == "Supplier"
|
||||
else "base_net_total"
|
||||
)
|
||||
voucher_wise_amount = {}
|
||||
vouchers = []
|
||||
|
||||
@@ -288,7 +296,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
|
||||
{"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")}
|
||||
)
|
||||
|
||||
invoices_details = frappe.get_all(doctype, filters=filters, fields=["name", "base_net_total"])
|
||||
invoices_details = frappe.get_all(doctype, filters=filters, fields=["name", field])
|
||||
|
||||
for d in invoices_details:
|
||||
vouchers.append(d.name)
|
||||
@@ -392,7 +400,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
|
||||
tds_amount = 0
|
||||
invoice_filters = {"name": ("in", vouchers), "docstatus": 1, "apply_tds": 1}
|
||||
|
||||
field = "sum(net_total)"
|
||||
field = "sum(tax_withholding_net_total)"
|
||||
|
||||
if cint(tax_details.consider_party_ledger_amount):
|
||||
invoice_filters.pop("apply_tds", None)
|
||||
@@ -415,12 +423,12 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
|
||||
)
|
||||
|
||||
supp_credit_amt += supp_jv_credit_amt
|
||||
supp_credit_amt += inv.net_total
|
||||
supp_credit_amt += inv.tax_withholding_net_total
|
||||
|
||||
threshold = tax_details.get("threshold", 0)
|
||||
cumulative_threshold = tax_details.get("cumulative_threshold", 0)
|
||||
|
||||
if (threshold and inv.net_total >= threshold) or (
|
||||
if (threshold and inv.tax_withholding_net_total >= threshold) or (
|
||||
cumulative_threshold and supp_credit_amt >= cumulative_threshold
|
||||
):
|
||||
if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(
|
||||
@@ -428,11 +436,11 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
|
||||
):
|
||||
# Get net total again as TDS is calculated on net total
|
||||
# Grand is used to just check for threshold breach
|
||||
net_total = 0
|
||||
if vouchers:
|
||||
net_total = frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(net_total)")
|
||||
|
||||
net_total += inv.net_total
|
||||
net_total = (
|
||||
frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(tax_withholding_net_total)")
|
||||
or 0.0
|
||||
)
|
||||
net_total += inv.tax_withholding_net_total
|
||||
supp_credit_amt = net_total - cumulative_threshold
|
||||
|
||||
if ldc and is_valid_certificate(
|
||||
@@ -440,7 +448,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
|
||||
ldc.valid_upto,
|
||||
inv.get("posting_date") or inv.get("transaction_date"),
|
||||
tax_deducted,
|
||||
inv.net_total,
|
||||
inv.tax_withholding_net_total,
|
||||
ldc.certificate_limit,
|
||||
):
|
||||
tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
|
||||
@@ -523,7 +531,7 @@ def get_tds_amount_from_ldc(ldc, parties, pan_no, tax_details, posting_date, net
|
||||
limit_consumed = frappe.db.get_value(
|
||||
"Purchase Invoice",
|
||||
{"supplier": ("in", parties), "apply_tds": 1, "docstatus": 1},
|
||||
"sum(net_total)",
|
||||
"sum(tax_withholding_net_total)",
|
||||
)
|
||||
|
||||
if is_valid_certificate(
|
||||
|
||||
@@ -186,6 +186,46 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
||||
for d in reversed(invoices):
|
||||
d.cancel()
|
||||
|
||||
def test_tds_calculation_on_net_total_partial_tds(self):
|
||||
frappe.db.set_value(
|
||||
"Supplier", "Test TDS Supplier4", "tax_withholding_category", "Cumulative Threshold TDS"
|
||||
)
|
||||
invoices = []
|
||||
|
||||
pi = create_purchase_invoice(supplier="Test TDS Supplier4", rate=20000, do_not_save=True)
|
||||
pi.extend(
|
||||
"items",
|
||||
[
|
||||
{
|
||||
"doctype": "Purchase Invoice Item",
|
||||
"item_code": frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name"),
|
||||
"qty": 1,
|
||||
"rate": 20000,
|
||||
"cost_center": "Main - _TC",
|
||||
"expense_account": "Stock Received But Not Billed - _TC",
|
||||
"apply_tds": 0,
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Invoice Item",
|
||||
"item_code": frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name"),
|
||||
"qty": 1,
|
||||
"rate": 35000,
|
||||
"cost_center": "Main - _TC",
|
||||
"expense_account": "Stock Received But Not Billed - _TC",
|
||||
"apply_tds": 1,
|
||||
},
|
||||
],
|
||||
)
|
||||
pi.save()
|
||||
pi.submit()
|
||||
invoices.append(pi)
|
||||
|
||||
self.assertEqual(pi.taxes[0].tax_amount, 5500)
|
||||
|
||||
# cancel invoices to avoid clashing
|
||||
for d in reversed(invoices):
|
||||
d.cancel()
|
||||
|
||||
def test_multi_category_single_supplier(self):
|
||||
frappe.db.set_value(
|
||||
"Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category"
|
||||
|
||||
@@ -227,7 +227,7 @@ class AccountsController(TransactionBase):
|
||||
for item in self.get("items"):
|
||||
if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"):
|
||||
if not item.get(field_map.get(self.doctype)):
|
||||
default_deferred_account = frappe.db.get_value(
|
||||
default_deferred_account = frappe.get_cached_value(
|
||||
"Company", self.company, "default_" + field_map.get(self.doctype)
|
||||
)
|
||||
if not default_deferred_account:
|
||||
@@ -676,7 +676,7 @@ class AccountsController(TransactionBase):
|
||||
|
||||
def validate_enabled_taxes_and_charges(self):
|
||||
taxes_and_charges_doctype = self.meta.get_options("taxes_and_charges")
|
||||
if frappe.db.get_value(taxes_and_charges_doctype, self.taxes_and_charges, "disabled"):
|
||||
if frappe.get_cached_value(taxes_and_charges_doctype, self.taxes_and_charges, "disabled"):
|
||||
frappe.throw(
|
||||
_("{0} '{1}' is disabled").format(taxes_and_charges_doctype, self.taxes_and_charges)
|
||||
)
|
||||
@@ -684,7 +684,7 @@ class AccountsController(TransactionBase):
|
||||
def validate_tax_account_company(self):
|
||||
for d in self.get("taxes"):
|
||||
if d.account_head:
|
||||
tax_account_company = frappe.db.get_value("Account", d.account_head, "company")
|
||||
tax_account_company = frappe.get_cached_value("Account", d.account_head, "company")
|
||||
if tax_account_company != self.company:
|
||||
frappe.throw(
|
||||
_("Row #{0}: Account {1} does not belong to company {2}").format(
|
||||
@@ -929,7 +929,9 @@ class AccountsController(TransactionBase):
|
||||
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.get_cached_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"))
|
||||
@@ -1026,7 +1028,7 @@ class AccountsController(TransactionBase):
|
||||
else self.grand_total
|
||||
),
|
||||
"outstanding_amount": self.outstanding_amount,
|
||||
"difference_account": frappe.db.get_value(
|
||||
"difference_account": frappe.get_cached_value(
|
||||
"Company", self.company, "exchange_gain_loss_account"
|
||||
),
|
||||
"exchange_gain_loss": flt(d.get("exchange_gain_loss")),
|
||||
@@ -1394,7 +1396,7 @@ class AccountsController(TransactionBase):
|
||||
@property
|
||||
def company_abbr(self):
|
||||
if not hasattr(self, "_abbr"):
|
||||
self._abbr = frappe.db.get_value("Company", self.company, "abbr")
|
||||
self._abbr = frappe.get_cached_value("Company", self.company, "abbr")
|
||||
|
||||
return self._abbr
|
||||
|
||||
@@ -1780,7 +1782,7 @@ class AccountsController(TransactionBase):
|
||||
"""
|
||||
|
||||
if self.is_internal_transfer() and not self.unrealized_profit_loss_account:
|
||||
unrealized_profit_loss_account = frappe.db.get_value(
|
||||
unrealized_profit_loss_account = frappe.get_cached_value(
|
||||
"Company", self.company, "unrealized_profit_loss_account"
|
||||
)
|
||||
|
||||
@@ -1895,7 +1897,9 @@ class AccountsController(TransactionBase):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_tax_rate(account_head):
|
||||
return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)
|
||||
return frappe.get_cached_value(
|
||||
"Account", account_head, ["tax_rate", "account_name"], as_dict=True
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@@ -1904,7 +1908,7 @@ def get_default_taxes_and_charges(master_doctype, tax_template=None, company=Non
|
||||
return {}
|
||||
|
||||
if tax_template and company:
|
||||
tax_template_company = frappe.db.get_value(master_doctype, tax_template, "company")
|
||||
tax_template_company = frappe.get_cached_value(master_doctype, tax_template, "company")
|
||||
if tax_template_company == company:
|
||||
return
|
||||
|
||||
|
||||
@@ -326,7 +326,7 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
|
||||
company = frappe.db.get_value("Delivery Note", source_name, "company")
|
||||
default_warehouse_for_sales_return = frappe.db.get_value(
|
||||
default_warehouse_for_sales_return = frappe.get_cached_value(
|
||||
"Company", company, "default_warehouse_for_sales_return"
|
||||
)
|
||||
|
||||
@@ -340,11 +340,11 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None):
|
||||
|
||||
# look for Print Heading "Credit Note"
|
||||
if not doc.select_print_heading:
|
||||
doc.select_print_heading = frappe.db.get_value("Print Heading", _("Credit Note"))
|
||||
doc.select_print_heading = frappe.get_cached_value("Print Heading", _("Credit Note"))
|
||||
|
||||
elif doctype == "Purchase Invoice":
|
||||
# look for Print Heading "Debit Note"
|
||||
doc.select_print_heading = frappe.db.get_value("Print Heading", _("Debit Note"))
|
||||
doc.select_print_heading = frappe.get_cached_value("Print Heading", _("Debit Note"))
|
||||
|
||||
for tax in doc.get("taxes") or []:
|
||||
if tax.charge_type == "Actual":
|
||||
|
||||
@@ -57,7 +57,7 @@ class StockController(AccountsController):
|
||||
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
|
||||
|
||||
provisional_accounting_for_non_stock_items = cint(
|
||||
frappe.db.get_value(
|
||||
frappe.get_cached_value(
|
||||
"Company", self.company, "enable_provisional_accounting_for_non_stock_items"
|
||||
)
|
||||
)
|
||||
@@ -200,7 +200,7 @@ class StockController(AccountsController):
|
||||
elif self.get("is_internal_supplier"):
|
||||
warehouse_asset_account = warehouse_account[item_row.get("warehouse")]["account"]
|
||||
|
||||
expense_account = frappe.db.get_value("Company", self.company, "default_expense_account")
|
||||
expense_account = frappe.get_cached_value("Company", self.company, "default_expense_account")
|
||||
|
||||
gl_list.append(
|
||||
self.get_gl_dict(
|
||||
@@ -235,7 +235,7 @@ class StockController(AccountsController):
|
||||
|
||||
if warehouse_with_no_account:
|
||||
for wh in warehouse_with_no_account:
|
||||
if frappe.db.get_value("Warehouse", wh, "company"):
|
||||
if frappe.get_cached_value("Warehouse", wh, "company"):
|
||||
frappe.throw(
|
||||
_(
|
||||
"Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}."
|
||||
@@ -449,15 +449,15 @@ class StockController(AccountsController):
|
||||
|
||||
# Get value based on doctype name
|
||||
if not sl_dict.get(dimension.target_fieldname):
|
||||
fieldname = frappe.get_cached_value(
|
||||
"DocField", {"parent": self.doctype, "options": dimension.fetch_from_parent}, "fieldname"
|
||||
fieldname = next(
|
||||
(
|
||||
field.fieldname
|
||||
for field in frappe.get_meta(self.doctype).fields
|
||||
if field.options == dimension.fetch_from_parent
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
if not fieldname:
|
||||
fieldname = frappe.get_cached_value(
|
||||
"Custom Field", {"dt": self.doctype, "options": dimension.fetch_from_parent}, "fieldname"
|
||||
)
|
||||
|
||||
if fieldname and self.get(fieldname):
|
||||
sl_dict[dimension.target_fieldname] = self.get(fieldname)
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ class SubcontractingController(StockController):
|
||||
and self._doc_before_save
|
||||
):
|
||||
for row in self._doc_before_save.get("items"):
|
||||
item_dict[row.name] = (row.item_code, row.qty)
|
||||
item_dict[row.name] = (row.item_code, row.received_qty or row.qty)
|
||||
|
||||
return item_dict
|
||||
|
||||
@@ -118,7 +118,9 @@ class SubcontractingController(StockController):
|
||||
|
||||
for row in self.items:
|
||||
self.__reference_name.append(row.name)
|
||||
if (row.name not in item_dict) or (row.item_code, row.qty) != item_dict[row.name]:
|
||||
if (row.name not in item_dict) or (row.item_code, row.received_qty or row.qty) != item_dict[
|
||||
row.name
|
||||
]:
|
||||
self.__changed_name.append(row.name)
|
||||
|
||||
if item_dict.get(row.name):
|
||||
@@ -461,12 +463,13 @@ class SubcontractingController(StockController):
|
||||
|
||||
def __get_qty_based_on_material_transfer(self, item_row, transfer_item):
|
||||
key = (item_row.item_code, item_row.get(self.subcontract_data.order_field))
|
||||
item_qty = item_row.received_qty or item_row.qty
|
||||
|
||||
if self.qty_to_be_received == item_row.qty:
|
||||
if self.qty_to_be_received.get(key) == item_qty:
|
||||
return transfer_item.qty
|
||||
|
||||
if self.qty_to_be_received:
|
||||
qty = (flt(item_row.qty) * flt(transfer_item.qty)) / flt(self.qty_to_be_received.get(key, 0))
|
||||
qty = (flt(item_qty) * flt(transfer_item.qty)) / flt(self.qty_to_be_received.get(key, 0))
|
||||
transfer_item.item_details.required_qty = transfer_item.qty
|
||||
|
||||
if transfer_item.serial_no or frappe.get_cached_value(
|
||||
@@ -491,7 +494,11 @@ class SubcontractingController(StockController):
|
||||
for bom_item in self.__get_materials_from_bom(
|
||||
row.item_code, row.bom, row.get("include_exploded_items")
|
||||
):
|
||||
qty = flt(bom_item.qty_consumed_per_unit) * flt(row.qty) * row.conversion_factor
|
||||
qty = (
|
||||
flt(bom_item.qty_consumed_per_unit)
|
||||
* flt(row.received_qty or row.qty)
|
||||
* row.conversion_factor
|
||||
)
|
||||
bom_item.main_item_code = row.item_code
|
||||
self.__update_reserve_warehouse(bom_item, row)
|
||||
self.__set_alternative_item(bom_item)
|
||||
|
||||
@@ -58,12 +58,25 @@ class calculate_taxes_and_totals(object):
|
||||
self.initialize_taxes()
|
||||
self.determine_exclusive_rate()
|
||||
self.calculate_net_total()
|
||||
self.calculate_tax_withholding_net_total()
|
||||
self.calculate_taxes()
|
||||
self.manipulate_grand_total_for_inclusive_tax()
|
||||
self.calculate_totals()
|
||||
self._cleanup()
|
||||
self.calculate_total_net_weight()
|
||||
|
||||
def calculate_tax_withholding_net_total(self):
|
||||
if hasattr(self.doc, "tax_withholding_net_total"):
|
||||
sum_net_amount = 0
|
||||
sum_base_net_amount = 0
|
||||
for item in self.doc.get("items"):
|
||||
if hasattr(item, "apply_tds") and item.apply_tds:
|
||||
sum_net_amount += item.net_amount
|
||||
sum_base_net_amount += item.base_net_amount
|
||||
|
||||
self.doc.tax_withholding_net_total = sum_net_amount
|
||||
self.doc.base_tax_withholding_net_total = sum_base_net_amount
|
||||
|
||||
def validate_item_tax_template(self):
|
||||
for item in self.doc.get("items"):
|
||||
if item.item_code and item.get("item_tax_template"):
|
||||
@@ -1043,7 +1056,7 @@ class init_landed_taxes_and_totals(object):
|
||||
company_currency = erpnext.get_company_currency(self.doc.company)
|
||||
for d in self.doc.get(self.tax_field):
|
||||
if not d.account_currency:
|
||||
account_currency = frappe.db.get_value("Account", d.expense_account, "account_currency")
|
||||
account_currency = frappe.get_cached_value("Account", d.expense_account, "account_currency")
|
||||
d.account_currency = account_currency or company_currency
|
||||
|
||||
def set_exchange_rate(self):
|
||||
|
||||
@@ -80,7 +80,7 @@ def get_data(filters, conditions):
|
||||
if conditions.get("trans") == "Quotation" and filters.get("group_by") == "Customer":
|
||||
cond += " and t1.quotation_to = 'Customer'"
|
||||
|
||||
year_start_date, year_end_date = frappe.db.get_value(
|
||||
year_start_date, year_end_date = frappe.get_cached_value(
|
||||
"Fiscal Year", filters.get("fiscal_year"), ["year_start_date", "year_end_date"]
|
||||
)
|
||||
|
||||
@@ -275,7 +275,7 @@ def get_period_date_ranges(period, fiscal_year=None, year_start_date=None):
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
if not year_start_date:
|
||||
year_start_date, year_end_date = frappe.db.get_value(
|
||||
year_start_date, year_end_date = frappe.get_cached_value(
|
||||
"Fiscal Year", fiscal_year, ["year_start_date", "year_end_date"]
|
||||
)
|
||||
|
||||
|
||||
@@ -589,66 +589,69 @@ erpnext.work_order = {
|
||||
}
|
||||
}
|
||||
|
||||
if(!frm.doc.skip_transfer){
|
||||
if (frm.doc.status != 'Stopped') {
|
||||
// If "Material Consumption is check in Manufacturing Settings, allow Material Consumption
|
||||
if (flt(doc.material_transferred_for_manufacturing) > 0 && frm.doc.status != 'Stopped') {
|
||||
if ((flt(doc.produced_qty) < flt(doc.material_transferred_for_manufacturing))) {
|
||||
frm.has_finish_btn = true;
|
||||
|
||||
if (frm.doc.__onload && frm.doc.__onload.material_consumption == 1) {
|
||||
// Only show "Material Consumption" when required_qty > consumed_qty
|
||||
var counter = 0;
|
||||
var tbl = frm.doc.required_items || [];
|
||||
var tbl_lenght = tbl.length;
|
||||
for (var i = 0, len = tbl_lenght; i < len; i++) {
|
||||
let wo_item_qty = frm.doc.required_items[i].transferred_qty || frm.doc.required_items[i].required_qty;
|
||||
if (flt(wo_item_qty) > flt(frm.doc.required_items[i].consumed_qty)) {
|
||||
counter += 1;
|
||||
}
|
||||
}
|
||||
if (counter > 0) {
|
||||
var consumption_btn = frm.add_custom_button(__('Material Consumption'), function() {
|
||||
const backflush_raw_materials_based_on = frm.doc.__onload.backflush_raw_materials_based_on;
|
||||
erpnext.work_order.make_consumption_se(frm, backflush_raw_materials_based_on);
|
||||
});
|
||||
consumption_btn.addClass('btn-primary');
|
||||
if (frm.doc.__onload && frm.doc.__onload.material_consumption == 1) {
|
||||
if (flt(doc.material_transferred_for_manufacturing) > 0 || frm.doc.skip_transfer) {
|
||||
// Only show "Material Consumption" when required_qty > consumed_qty
|
||||
var counter = 0;
|
||||
var tbl = frm.doc.required_items || [];
|
||||
var tbl_lenght = tbl.length;
|
||||
for (var i = 0, len = tbl_lenght; i < len; i++) {
|
||||
let wo_item_qty = frm.doc.required_items[i].transferred_qty || frm.doc.required_items[i].required_qty;
|
||||
if (flt(wo_item_qty) > flt(frm.doc.required_items[i].consumed_qty)) {
|
||||
counter += 1;
|
||||
}
|
||||
}
|
||||
if (counter > 0) {
|
||||
var consumption_btn = frm.add_custom_button(__('Material Consumption'), function() {
|
||||
const backflush_raw_materials_based_on = frm.doc.__onload.backflush_raw_materials_based_on;
|
||||
erpnext.work_order.make_consumption_se(frm, backflush_raw_materials_based_on);
|
||||
});
|
||||
consumption_btn.addClass('btn-primary');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!frm.doc.skip_transfer){
|
||||
if (flt(doc.material_transferred_for_manufacturing) > 0) {
|
||||
if ((flt(doc.produced_qty) < flt(doc.material_transferred_for_manufacturing))) {
|
||||
frm.has_finish_btn = true;
|
||||
|
||||
var finish_btn = frm.add_custom_button(__('Finish'), function() {
|
||||
erpnext.work_order.make_se(frm, 'Manufacture');
|
||||
});
|
||||
|
||||
if(doc.material_transferred_for_manufacturing>=doc.qty) {
|
||||
// all materials transferred for manufacturing, make this primary
|
||||
finish_btn.addClass('btn-primary');
|
||||
}
|
||||
} else {
|
||||
frappe.db.get_doc("Manufacturing Settings").then((doc) => {
|
||||
let allowance_percentage = doc.overproduction_percentage_for_work_order;
|
||||
|
||||
if (allowance_percentage > 0) {
|
||||
let allowed_qty = frm.doc.qty + ((allowance_percentage / 100) * frm.doc.qty);
|
||||
|
||||
if ((flt(doc.produced_qty) < allowed_qty)) {
|
||||
frm.add_custom_button(__('Finish'), function() {
|
||||
erpnext.work_order.make_se(frm, 'Manufacture');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((flt(doc.produced_qty) < flt(doc.qty))) {
|
||||
var finish_btn = frm.add_custom_button(__('Finish'), function() {
|
||||
erpnext.work_order.make_se(frm, 'Manufacture');
|
||||
});
|
||||
|
||||
if(doc.material_transferred_for_manufacturing>=doc.qty) {
|
||||
// all materials transferred for manufacturing, make this primary
|
||||
finish_btn.addClass('btn-primary');
|
||||
}
|
||||
} else {
|
||||
frappe.db.get_doc("Manufacturing Settings").then((doc) => {
|
||||
let allowance_percentage = doc.overproduction_percentage_for_work_order;
|
||||
|
||||
if (allowance_percentage > 0) {
|
||||
let allowed_qty = frm.doc.qty + ((allowance_percentage / 100) * frm.doc.qty);
|
||||
|
||||
if ((flt(doc.produced_qty) < allowed_qty)) {
|
||||
frm.add_custom_button(__('Finish'), function() {
|
||||
erpnext.work_order.make_se(frm, 'Manufacture');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
finish_btn.addClass('btn-primary');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((flt(doc.produced_qty) < flt(doc.qty)) && frm.doc.status != 'Stopped') {
|
||||
var finish_btn = frm.add_custom_button(__('Finish'), function() {
|
||||
erpnext.work_order.make_se(frm, 'Manufacture');
|
||||
});
|
||||
finish_btn.addClass('btn-primary');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
calculate_cost: function(doc) {
|
||||
if (doc.operations){
|
||||
|
||||
@@ -317,4 +317,4 @@ erpnext.patches.v14_0.fix_subcontracting_receipt_gl_entries
|
||||
erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger
|
||||
erpnext.patches.v14_0.create_accounting_dimensions_for_asset_capitalization
|
||||
erpnext.patches.v13_0.update_schedule_type_in_loans
|
||||
|
||||
erpnext.patches.v14_0.update_tds_fields
|
||||
29
erpnext/patches/v14_0/update_tds_fields.py
Normal file
29
erpnext/patches/v14_0/update_tds_fields.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import frappe
|
||||
from frappe.utils import nowdate
|
||||
|
||||
from erpnext.accounts.utils import FiscalYearError, get_fiscal_year
|
||||
|
||||
|
||||
def execute():
|
||||
# Only do for current fiscal year, no need to repost for all years
|
||||
for company in frappe.get_all("Company"):
|
||||
try:
|
||||
fiscal_year_details = get_fiscal_year(date=nowdate(), company=company.name, as_dict=True)
|
||||
|
||||
purchase_invoice = frappe.qb.DocType("Purchase Invoice")
|
||||
|
||||
frappe.qb.update(purchase_invoice).set(
|
||||
purchase_invoice.tax_withholding_net_total, purchase_invoice.net_total
|
||||
).set(
|
||||
purchase_invoice.base_tax_withholding_net_total, purchase_invoice.base_net_total
|
||||
).where(
|
||||
purchase_invoice.company == company.name
|
||||
).where(
|
||||
purchase_invoice.apply_tds == 1
|
||||
).where(
|
||||
purchase_invoice.posting_date >= fiscal_year_details.year_start_date
|
||||
).where(
|
||||
purchase_invoice.docstatus == 1
|
||||
).run()
|
||||
except FiscalYearError:
|
||||
pass
|
||||
@@ -1207,7 +1207,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
"base_rounding_adjustment"], company_currency);
|
||||
|
||||
this.frm.set_currency_labels(["total", "net_total", "total_taxes_and_charges", "discount_amount",
|
||||
"grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted",
|
||||
"grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted","tax_withholding_net_total",
|
||||
"rounded_total", "in_words", "paid_amount", "write_off_amount", "operating_cost",
|
||||
"scrap_material_cost", "rounding_adjustment", "raw_material_cost",
|
||||
"total_cost"], this.frm.doc.currency);
|
||||
@@ -1224,7 +1224,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
}
|
||||
|
||||
// toggle fields
|
||||
this.frm.toggle_display(["conversion_rate", "base_total", "base_net_total",
|
||||
this.frm.toggle_display(["conversion_rate", "base_total", "base_net_total", "base_tax_withholding_net_total",
|
||||
"base_total_taxes_and_charges", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted",
|
||||
"base_grand_total", "base_rounded_total", "base_in_words", "base_discount_amount",
|
||||
"base_paid_amount", "base_write_off_amount", "base_operating_cost", "base_raw_material_cost",
|
||||
|
||||
@@ -91,8 +91,8 @@ def get_columns(filters):
|
||||
columns = [
|
||||
_("Item") + ":Link/Item:180",
|
||||
_("Item Group") + "::100",
|
||||
_("Value") + ":Currency:100",
|
||||
_("Age") + ":Float:60",
|
||||
_("Value") + ":Currency:120",
|
||||
_("Age") + ":Float:80",
|
||||
]
|
||||
return columns
|
||||
|
||||
@@ -123,7 +123,7 @@ def get_warehouse_list(filters):
|
||||
|
||||
def add_warehouse_column(columns, warehouse_list):
|
||||
if len(warehouse_list) > 1:
|
||||
columns += [_("Total Qty") + ":Int:50"]
|
||||
columns += [_("Total Qty") + ":Int:90"]
|
||||
|
||||
for wh in warehouse_list:
|
||||
columns += [_(wh.name) + ":Int:54"]
|
||||
columns += [_(wh.name) + ":Int:120"]
|
||||
|
||||
@@ -58,6 +58,7 @@ class SubcontractingReceipt(SubcontractingController):
|
||||
def before_validate(self):
|
||||
super(SubcontractingReceipt, self).before_validate()
|
||||
self.set_items_bom()
|
||||
self.set_received_qty()
|
||||
self.set_items_cost_center()
|
||||
self.set_items_expense_account()
|
||||
|
||||
@@ -212,6 +213,10 @@ class SubcontractingReceipt(SubcontractingController):
|
||||
"bom",
|
||||
)
|
||||
|
||||
def set_received_qty(self):
|
||||
for item in self.items:
|
||||
item.received_qty = flt(item.qty) + flt(item.rejected_qty)
|
||||
|
||||
def set_items_cost_center(self):
|
||||
if self.company:
|
||||
cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
|
||||
|
||||
@@ -468,6 +468,65 @@ class TestSubcontractingReceipt(FrappeTestCase):
|
||||
scr.cancel()
|
||||
self.assertTrue(get_gl_entries("Subcontracting Receipt", scr.name))
|
||||
|
||||
def test_supplied_items_consumed_qty(self):
|
||||
# Set Backflush Based On as "Material Transferred for Subcontracting" to transfer RM's more than the required qty
|
||||
set_backflush_based_on("Material Transferred for Subcontract")
|
||||
|
||||
# Create Material Receipt for RM's
|
||||
make_stock_entry(
|
||||
item_code="_Test Item", qty=100, target="_Test Warehouse 1 - _TC", basic_rate=100
|
||||
)
|
||||
make_stock_entry(
|
||||
item_code="_Test Item Home Desktop 100",
|
||||
qty=100,
|
||||
target="_Test Warehouse 1 - _TC",
|
||||
basic_rate=100,
|
||||
)
|
||||
|
||||
service_items = [
|
||||
{
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"item_code": "Subcontracted Service Item 1",
|
||||
"qty": 10,
|
||||
"rate": 100,
|
||||
"fg_item": "_Test FG Item",
|
||||
"fg_item_qty": 10,
|
||||
},
|
||||
]
|
||||
|
||||
# Create Subcontracting Order
|
||||
sco = get_subcontracting_order(service_items=service_items)
|
||||
|
||||
# Transfer RM's
|
||||
rm_items = get_rm_items(sco.supplied_items)
|
||||
rm_items[0]["qty"] = 20 # Extra 10 Qty
|
||||
itemwise_details = make_stock_in_entry(rm_items=rm_items)
|
||||
make_stock_transfer_entry(
|
||||
sco_no=sco.name,
|
||||
rm_items=rm_items,
|
||||
itemwise_details=copy.deepcopy(itemwise_details),
|
||||
)
|
||||
|
||||
# Create Subcontracting Receipt
|
||||
scr = make_subcontracting_receipt(sco.name)
|
||||
scr.rejected_warehouse = "_Test Warehouse 1 - _TC"
|
||||
|
||||
scr.items[0].qty = 5 # Accepted Qty
|
||||
scr.items[0].rejected_qty = 3
|
||||
scr.save()
|
||||
|
||||
# consumed_qty should be ((received_qty) * (transfered_qty / qty)) = ((5 + 3) * (20 / 10)) = 16
|
||||
self.assertEqual(scr.supplied_items[0].consumed_qty, 16)
|
||||
|
||||
# Set Backflush Based On as "BOM"
|
||||
set_backflush_based_on("BOM")
|
||||
|
||||
scr.items[0].rejected_qty = 4
|
||||
scr.save()
|
||||
|
||||
# consumed_qty should be ((received_qty) * (qty_consumed_per_unit)) = ((5 + 4) * (1)) = 9
|
||||
self.assertEqual(scr.supplied_items[0].consumed_qty, 9)
|
||||
|
||||
|
||||
def make_return_subcontracting_receipt(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
Reference in New Issue
Block a user