Merge branch 'develop' into cont-temp
This commit is contained in:
commit
13dcd18ede
@ -155,7 +155,8 @@ class OpeningInvoiceCreationTool(Document):
|
|||||||
"posting_date": row.posting_date,
|
"posting_date": row.posting_date,
|
||||||
frappe.scrub(row.party_type): row.party,
|
frappe.scrub(row.party_type): row.party,
|
||||||
"is_pos": 0,
|
"is_pos": 0,
|
||||||
"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice"
|
"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice",
|
||||||
|
"update_stock": 0
|
||||||
})
|
})
|
||||||
|
|
||||||
accounting_dimension = get_accounting_dimensions()
|
accounting_dimension = get_accounting_dimensions()
|
||||||
|
@ -7,17 +7,24 @@ import frappe
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
test_dependencies = ["Customer", "Supplier"]
|
test_dependencies = ["Customer", "Supplier"]
|
||||||
|
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
||||||
from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import get_temporary_opening_account
|
from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import get_temporary_opening_account
|
||||||
|
|
||||||
class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
||||||
def make_invoices(self, invoice_type="Sales"):
|
def setUp(self):
|
||||||
|
if not frappe.db.exists("Company", "_Test Opening Invoice Company"):
|
||||||
|
make_company()
|
||||||
|
|
||||||
|
def make_invoices(self, invoice_type="Sales", company=None, party_1=None, party_2=None):
|
||||||
doc = frappe.get_single("Opening Invoice Creation Tool")
|
doc = frappe.get_single("Opening Invoice Creation Tool")
|
||||||
args = get_opening_invoice_creation_dict(invoice_type=invoice_type)
|
args = get_opening_invoice_creation_dict(invoice_type=invoice_type, company=company,
|
||||||
|
party_1=party_1, party_2=party_2)
|
||||||
doc.update(args)
|
doc.update(args)
|
||||||
return doc.make_invoices()
|
return doc.make_invoices()
|
||||||
|
|
||||||
def test_opening_sales_invoice_creation(self):
|
def test_opening_sales_invoice_creation(self):
|
||||||
invoices = self.make_invoices()
|
property_setter = make_property_setter("Sales Invoice", "update_stock", "default", 1, "Check")
|
||||||
|
invoices = self.make_invoices(company="_Test Opening Invoice Company")
|
||||||
|
|
||||||
self.assertEqual(len(invoices), 2)
|
self.assertEqual(len(invoices), 2)
|
||||||
expected_value = {
|
expected_value = {
|
||||||
@ -27,6 +34,13 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
self.check_expected_values(invoices, expected_value)
|
self.check_expected_values(invoices, expected_value)
|
||||||
|
|
||||||
|
si = frappe.get_doc("Sales Invoice", invoices[0])
|
||||||
|
|
||||||
|
# Check if update stock is not enabled
|
||||||
|
self.assertEqual(si.update_stock, 0)
|
||||||
|
|
||||||
|
property_setter.delete()
|
||||||
|
|
||||||
def check_expected_values(self, invoices, expected_value, invoice_type="Sales"):
|
def check_expected_values(self, invoices, expected_value, invoice_type="Sales"):
|
||||||
doctype = "Sales Invoice" if invoice_type == "Sales" else "Purchase Invoice"
|
doctype = "Sales Invoice" if invoice_type == "Sales" else "Purchase Invoice"
|
||||||
|
|
||||||
@ -36,7 +50,7 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
|||||||
self.assertEqual(si.get(field, ""), expected_value[invoice_idx][field_idx])
|
self.assertEqual(si.get(field, ""), expected_value[invoice_idx][field_idx])
|
||||||
|
|
||||||
def test_opening_purchase_invoice_creation(self):
|
def test_opening_purchase_invoice_creation(self):
|
||||||
invoices = self.make_invoices(invoice_type="Purchase")
|
invoices = self.make_invoices(invoice_type="Purchase", company="_Test Opening Invoice Company")
|
||||||
|
|
||||||
self.assertEqual(len(invoices), 2)
|
self.assertEqual(len(invoices), 2)
|
||||||
expected_value = {
|
expected_value = {
|
||||||
@ -46,6 +60,32 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
self.check_expected_values(invoices, expected_value, "Purchase")
|
self.check_expected_values(invoices, expected_value, "Purchase")
|
||||||
|
|
||||||
|
def test_opening_sales_invoice_creation_with_missing_debit_account(self):
|
||||||
|
company = "_Test Opening Invoice Company"
|
||||||
|
party_1, party_2 = make_customer("Customer A"), make_customer("Customer B")
|
||||||
|
|
||||||
|
old_default_receivable_account = frappe.db.get_value("Company", company, "default_receivable_account")
|
||||||
|
frappe.db.set_value("Company", company, "default_receivable_account", "")
|
||||||
|
|
||||||
|
if not frappe.db.exists("Cost Center", "_Test Opening Invoice Company - _TOIC"):
|
||||||
|
cc = frappe.get_doc({"doctype": "Cost Center", "cost_center_name": "_Test Opening Invoice Company",
|
||||||
|
"is_group": 1, "company": "_Test Opening Invoice Company"})
|
||||||
|
cc.insert(ignore_mandatory=True)
|
||||||
|
cc2 = frappe.get_doc({"doctype": "Cost Center", "cost_center_name": "Main", "is_group": 0,
|
||||||
|
"company": "_Test Opening Invoice Company", "parent_cost_center": cc.name})
|
||||||
|
cc2.insert()
|
||||||
|
|
||||||
|
frappe.db.set_value("Company", company, "cost_center", "Main - _TOIC")
|
||||||
|
|
||||||
|
self.make_invoices(company="_Test Opening Invoice Company", party_1=party_1, party_2=party_2)
|
||||||
|
|
||||||
|
# Check if missing debit account error raised
|
||||||
|
error_log = frappe.db.exists("Error Log", {"error": ["like", "%erpnext.controllers.accounts_controller.AccountMissingError%"]})
|
||||||
|
self.assertTrue(error_log)
|
||||||
|
|
||||||
|
# teardown
|
||||||
|
frappe.db.set_value("Company", company, "default_receivable_account", old_default_receivable_account)
|
||||||
|
|
||||||
def get_opening_invoice_creation_dict(**args):
|
def get_opening_invoice_creation_dict(**args):
|
||||||
party = "Customer" if args.get("invoice_type", "Sales") == "Sales" else "Supplier"
|
party = "Customer" if args.get("invoice_type", "Sales") == "Sales" else "Supplier"
|
||||||
company = args.get("company", "_Test Company")
|
company = args.get("company", "_Test Company")
|
||||||
@ -57,7 +97,7 @@ def get_opening_invoice_creation_dict(**args):
|
|||||||
{
|
{
|
||||||
"qty": 1.0,
|
"qty": 1.0,
|
||||||
"outstanding_amount": 300,
|
"outstanding_amount": 300,
|
||||||
"party": "_Test {0}".format(party),
|
"party": args.get("party_1") or "_Test {0}".format(party),
|
||||||
"item_name": "Opening Item",
|
"item_name": "Opening Item",
|
||||||
"due_date": "2016-09-10",
|
"due_date": "2016-09-10",
|
||||||
"posting_date": "2016-09-05",
|
"posting_date": "2016-09-05",
|
||||||
@ -66,7 +106,7 @@ def get_opening_invoice_creation_dict(**args):
|
|||||||
{
|
{
|
||||||
"qty": 2.0,
|
"qty": 2.0,
|
||||||
"outstanding_amount": 250,
|
"outstanding_amount": 250,
|
||||||
"party": "_Test {0} 1".format(party),
|
"party": args.get("party_2") or "_Test {0} 1".format(party),
|
||||||
"item_name": "Opening Item",
|
"item_name": "Opening Item",
|
||||||
"due_date": "2016-09-10",
|
"due_date": "2016-09-10",
|
||||||
"posting_date": "2016-09-05",
|
"posting_date": "2016-09-05",
|
||||||
@ -77,3 +117,30 @@ def get_opening_invoice_creation_dict(**args):
|
|||||||
|
|
||||||
invoice_dict.update(args)
|
invoice_dict.update(args)
|
||||||
return invoice_dict
|
return invoice_dict
|
||||||
|
|
||||||
|
def make_company():
|
||||||
|
if frappe.db.exists("Company", "_Test Opening Invoice Company"):
|
||||||
|
return frappe.get_doc("Company", "_Test Opening Invoice Company")
|
||||||
|
|
||||||
|
company = frappe.new_doc("Company")
|
||||||
|
company.company_name = "_Test Opening Invoice Company"
|
||||||
|
company.abbr = "_TOIC"
|
||||||
|
company.default_currency = "INR"
|
||||||
|
company.country = "India"
|
||||||
|
company.insert()
|
||||||
|
return company
|
||||||
|
|
||||||
|
def make_customer(customer=None):
|
||||||
|
customer_name = customer or "Opening Customer"
|
||||||
|
customer = frappe.get_doc({
|
||||||
|
"doctype": "Customer",
|
||||||
|
"customer_name": customer_name,
|
||||||
|
"customer_group": "All Customer Groups",
|
||||||
|
"customer_type": "Company",
|
||||||
|
"territory": "All Territories"
|
||||||
|
})
|
||||||
|
if not frappe.db.exists("Customer", customer_name):
|
||||||
|
customer.insert(ignore_permissions=True)
|
||||||
|
return customer.name
|
||||||
|
else:
|
||||||
|
return frappe.db.exists("Customer", customer_name)
|
@ -406,6 +406,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "0",
|
||||||
"depends_on": "eval:doc.rate_or_discount==\"Rate\"",
|
"depends_on": "eval:doc.rate_or_discount==\"Rate\"",
|
||||||
"fieldname": "rate",
|
"fieldname": "rate",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
@ -469,6 +470,7 @@
|
|||||||
"options": "UOM"
|
"options": "UOM"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"description": "If rate is zero them item will be treated as \"Free Item\"",
|
||||||
"fieldname": "free_item_rate",
|
"fieldname": "free_item_rate",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Rate"
|
"label": "Rate"
|
||||||
@ -563,7 +565,7 @@
|
|||||||
"icon": "fa fa-gift",
|
"icon": "fa fa-gift",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-10-28 16:53:14.416172",
|
"modified": "2020-12-04 00:36:24.698219",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Pricing Rule",
|
"name": "Pricing Rule",
|
||||||
|
@ -521,6 +521,22 @@ class TestPricingRule(unittest.TestCase):
|
|||||||
frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete()
|
frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete()
|
||||||
item.delete()
|
item.delete()
|
||||||
|
|
||||||
|
def test_pricing_rule_for_transaction(self):
|
||||||
|
make_item("Water Flask 1")
|
||||||
|
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
|
||||||
|
make_pricing_rule(selling=1, min_qty=5, price_or_product_discount="Product",
|
||||||
|
apply_on="Transaction", free_item="Water Flask 1", free_qty=1, free_item_rate=10)
|
||||||
|
|
||||||
|
si = create_sales_invoice(qty=5, do_not_submit=True)
|
||||||
|
self.assertEquals(len(si.items), 2)
|
||||||
|
self.assertEquals(si.items[1].rate, 10)
|
||||||
|
|
||||||
|
si1 = create_sales_invoice(qty=2, do_not_submit=True)
|
||||||
|
self.assertEquals(len(si1.items), 1)
|
||||||
|
|
||||||
|
for doc in [si, si1]:
|
||||||
|
doc.delete()
|
||||||
|
|
||||||
def make_pricing_rule(**args):
|
def make_pricing_rule(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|
||||||
@ -539,20 +555,23 @@ def make_pricing_rule(**args):
|
|||||||
"rate_or_discount": args.rate_or_discount or "Discount Percentage",
|
"rate_or_discount": args.rate_or_discount or "Discount Percentage",
|
||||||
"discount_percentage": args.discount_percentage or 0.0,
|
"discount_percentage": args.discount_percentage or 0.0,
|
||||||
"rate": args.rate or 0.0,
|
"rate": args.rate or 0.0,
|
||||||
"margin_type": args.margin_type,
|
|
||||||
"margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
|
"margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
|
||||||
"condition": args.condition or '',
|
"condition": args.condition or '',
|
||||||
"apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0
|
"apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0
|
||||||
})
|
})
|
||||||
|
|
||||||
if args.get("priority"):
|
for field in ["free_item", "free_qty", "free_item_rate", "priority",
|
||||||
doc.priority = args.get("priority")
|
"margin_type", "price_or_product_discount"]:
|
||||||
|
if args.get(field):
|
||||||
|
doc.set(field, args.get(field))
|
||||||
|
|
||||||
apply_on = doc.apply_on.replace(' ', '_').lower()
|
apply_on = doc.apply_on.replace(' ', '_').lower()
|
||||||
child_table = {'Item Code': 'items', 'Item Group': 'item_groups', 'Brand': 'brands'}
|
child_table = {'Item Code': 'items', 'Item Group': 'item_groups', 'Brand': 'brands'}
|
||||||
doc.append(child_table.get(doc.apply_on), {
|
|
||||||
apply_on: args.get(apply_on) or "_Test Item"
|
if doc.apply_on != "Transaction":
|
||||||
})
|
doc.append(child_table.get(doc.apply_on), {
|
||||||
|
apply_on: args.get(apply_on) or "_Test Item"
|
||||||
|
})
|
||||||
|
|
||||||
doc.insert(ignore_permissions=True)
|
doc.insert(ignore_permissions=True)
|
||||||
if args.get(apply_on) and apply_on != "item_code":
|
if args.get(apply_on) and apply_on != "item_code":
|
||||||
|
@ -457,6 +457,9 @@ def apply_pricing_rule_on_transaction(doc):
|
|||||||
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,
|
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,
|
||||||
doc.total, pricing_rules)
|
doc.total, pricing_rules)
|
||||||
|
|
||||||
|
if not pricing_rules:
|
||||||
|
remove_free_item(doc)
|
||||||
|
|
||||||
for d in pricing_rules:
|
for d in pricing_rules:
|
||||||
if d.price_or_product_discount == 'Price':
|
if d.price_or_product_discount == 'Price':
|
||||||
if d.apply_discount_on:
|
if d.apply_discount_on:
|
||||||
@ -480,6 +483,12 @@ def apply_pricing_rule_on_transaction(doc):
|
|||||||
get_product_discount_rule(d, item_details, doc=doc)
|
get_product_discount_rule(d, item_details, doc=doc)
|
||||||
apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
|
apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
|
||||||
doc.set_missing_values()
|
doc.set_missing_values()
|
||||||
|
doc.calculate_taxes_and_totals()
|
||||||
|
|
||||||
|
def remove_free_item(doc):
|
||||||
|
for d in doc.items:
|
||||||
|
if d.is_free_item:
|
||||||
|
doc.remove(d)
|
||||||
|
|
||||||
def get_applied_pricing_rules(pricing_rules):
|
def get_applied_pricing_rules(pricing_rules):
|
||||||
if pricing_rules:
|
if pricing_rules:
|
||||||
@ -492,7 +501,7 @@ def get_applied_pricing_rules(pricing_rules):
|
|||||||
|
|
||||||
def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
||||||
free_item = pricing_rule.free_item
|
free_item = pricing_rule.free_item
|
||||||
if pricing_rule.same_item:
|
if pricing_rule.same_item and pricing_rule.get("apply_on") != 'Transaction':
|
||||||
free_item = item_details.item_code or args.item_code
|
free_item = item_details.item_code or args.item_code
|
||||||
|
|
||||||
if not free_item:
|
if not free_item:
|
||||||
|
@ -21,7 +21,7 @@ class TestProcessDeferredAccounting(unittest.TestCase):
|
|||||||
item.no_of_months = 12
|
item.no_of_months = 12
|
||||||
item.save()
|
item.save()
|
||||||
|
|
||||||
si = create_sales_invoice(item=item.name, posting_date="2019-01-10", do_not_submit=True)
|
si = create_sales_invoice(item=item.name, update_stock=0, posting_date="2019-01-10", do_not_submit=True)
|
||||||
si.items[0].enable_deferred_revenue = 1
|
si.items[0].enable_deferred_revenue = 1
|
||||||
si.items[0].service_start_date = "2019-01-10"
|
si.items[0].service_start_date = "2019-01-10"
|
||||||
si.items[0].service_end_date = "2019-03-15"
|
si.items[0].service_end_date = "2019-03-15"
|
||||||
|
@ -147,6 +147,11 @@ class PurchaseInvoice(BuyingController):
|
|||||||
throw(_("Conversion rate cannot be 0 or 1"))
|
throw(_("Conversion rate cannot be 0 or 1"))
|
||||||
|
|
||||||
def validate_credit_to_acc(self):
|
def validate_credit_to_acc(self):
|
||||||
|
if not self.credit_to:
|
||||||
|
self.credit_to = get_party_account("Supplier", self.supplier, self.company)
|
||||||
|
if not self.credit_to:
|
||||||
|
self.raise_missing_debit_credit_account_error("Supplier", self.supplier)
|
||||||
|
|
||||||
account = frappe.db.get_value("Account", self.credit_to,
|
account = frappe.db.get_value("Account", self.credit_to,
|
||||||
["account_type", "report_type", "account_currency"], as_dict=True)
|
["account_type", "report_type", "account_currency"], as_dict=True)
|
||||||
|
|
||||||
@ -1032,7 +1037,9 @@ class PurchaseInvoice(BuyingController):
|
|||||||
updated_pr += update_billed_amount_based_on_po(d.po_detail, update_modified)
|
updated_pr += update_billed_amount_based_on_po(d.po_detail, update_modified)
|
||||||
|
|
||||||
for pr in set(updated_pr):
|
for pr in set(updated_pr):
|
||||||
frappe.get_doc("Purchase Receipt", pr).update_billing_percentage(update_modified=update_modified)
|
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billing_percentage
|
||||||
|
pr_doc = frappe.get_doc("Purchase Receipt", pr)
|
||||||
|
update_billing_percentage(pr_doc, update_modified=update_modified)
|
||||||
|
|
||||||
def on_recurring(self, reference_doc, auto_repeat_doc):
|
def on_recurring(self, reference_doc, auto_repeat_doc):
|
||||||
self.due_date = None
|
self.due_date = None
|
||||||
|
@ -472,6 +472,11 @@ class SalesInvoice(SellingController):
|
|||||||
return frappe.db.sql("select abbr from tabCompany where name=%s", self.company)[0][0]
|
return frappe.db.sql("select abbr from tabCompany where name=%s", self.company)[0][0]
|
||||||
|
|
||||||
def validate_debit_to_acc(self):
|
def validate_debit_to_acc(self):
|
||||||
|
if not self.debit_to:
|
||||||
|
self.debit_to = get_party_account("Customer", self.customer, self.company)
|
||||||
|
if not self.debit_to:
|
||||||
|
self.raise_missing_debit_credit_account_error("Customer", self.customer)
|
||||||
|
|
||||||
account = frappe.get_cached_value("Account", self.debit_to,
|
account = frappe.get_cached_value("Account", self.debit_to,
|
||||||
["account_type", "report_type", "account_currency"], as_dict=True)
|
["account_type", "report_type", "account_currency"], as_dict=True)
|
||||||
|
|
||||||
|
@ -14,11 +14,93 @@ def execute(filters=None):
|
|||||||
|
|
||||||
def get_column():
|
def get_column():
|
||||||
return [
|
return [
|
||||||
_("Delivery Note") + ":Link/Delivery Note:120", _("Status") + "::120", _("Date") + ":Date:100",
|
{
|
||||||
_("Suplier") + ":Link/Customer:120", _("Customer Name") + "::120",
|
"label": _("Delivery Note"),
|
||||||
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
|
"fieldname": "name",
|
||||||
_("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Pending Amount") + ":Currency:100",
|
"fieldtype": "Link",
|
||||||
_("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120",
|
"options": "Delivery Note",
|
||||||
|
"width": 160
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Date"),
|
||||||
|
"fieldname": "date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Customer"),
|
||||||
|
"fieldname": "customer",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Customer",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Customer Name"),
|
||||||
|
"fieldname": "customer_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Item Code"),
|
||||||
|
"fieldname": "item_code",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Item",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Amount"),
|
||||||
|
"fieldname": "amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 100,
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Billed Amount"),
|
||||||
|
"fieldname": "billed_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 100,
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Returned Amount"),
|
||||||
|
"fieldname": "returned_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 120,
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Pending Amount"),
|
||||||
|
"fieldname": "pending_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 120,
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Item Name"),
|
||||||
|
"fieldname": "item_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Description"),
|
||||||
|
"fieldname": "description",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Project"),
|
||||||
|
"fieldname": "project",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Project",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Company"),
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Company",
|
||||||
|
"width": 120
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_args():
|
def get_args():
|
||||||
|
@ -17,18 +17,26 @@ def get_ordered_to_be_billed_data(args):
|
|||||||
|
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
Select
|
Select
|
||||||
`{parent_tab}`.name, `{parent_tab}`.status, `{parent_tab}`.{date_field}, `{parent_tab}`.{party}, `{parent_tab}`.{party}_name,
|
`{parent_tab}`.name, `{parent_tab}`.{date_field},
|
||||||
{project_field}, `{child_tab}`.item_code, `{child_tab}`.base_amount,
|
`{parent_tab}`.{party}, `{parent_tab}`.{party}_name,
|
||||||
|
`{child_tab}`.item_code,
|
||||||
|
`{child_tab}`.base_amount,
|
||||||
(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)),
|
(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)),
|
||||||
(`{child_tab}`.base_amount - (`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1))),
|
(`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0)),
|
||||||
`{child_tab}`.item_name, `{child_tab}`.description, `{parent_tab}`.company
|
(`{child_tab}`.base_amount -
|
||||||
|
(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)) -
|
||||||
|
(`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0))),
|
||||||
|
`{child_tab}`.item_name, `{child_tab}`.description,
|
||||||
|
{project_field}, `{parent_tab}`.company
|
||||||
from
|
from
|
||||||
`{parent_tab}`, `{child_tab}`
|
`{parent_tab}`, `{child_tab}`
|
||||||
where
|
where
|
||||||
`{parent_tab}`.name = `{child_tab}`.parent and `{parent_tab}`.docstatus = 1
|
`{parent_tab}`.name = `{child_tab}`.parent and `{parent_tab}`.docstatus = 1
|
||||||
and `{parent_tab}`.status not in ('Closed', 'Completed')
|
and `{parent_tab}`.status not in ('Closed', 'Completed')
|
||||||
and `{child_tab}`.amount > 0 and round(`{child_tab}`.billed_amt *
|
and `{child_tab}`.amount > 0
|
||||||
ifnull(`{parent_tab}`.conversion_rate, 1), {precision}) < `{child_tab}`.base_amount
|
and (`{child_tab}`.base_amount -
|
||||||
|
round(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1), {precision}) -
|
||||||
|
(`{child_tab}`.base_rate * ifnull(`{child_tab}`.returned_qty, 0))) > 0
|
||||||
order by
|
order by
|
||||||
`{parent_tab}`.{order} {order_by}
|
`{parent_tab}`.{order} {order_by}
|
||||||
""".format(parent_tab = 'tab' + doctype, child_tab = 'tab' + child_tab, precision= precision, party = party,
|
""".format(parent_tab = 'tab' + doctype, child_tab = 'tab' + child_tab, precision= precision, party = party,
|
||||||
|
@ -14,11 +14,93 @@ def execute(filters=None):
|
|||||||
|
|
||||||
def get_column():
|
def get_column():
|
||||||
return [
|
return [
|
||||||
_("Purchase Receipt") + ":Link/Purchase Receipt:120", _("Status") + "::120", _("Date") + ":Date:100",
|
{
|
||||||
_("Supplier") + ":Link/Supplier:120", _("Supplier Name") + "::120",
|
"label": _("Purchase Receipt"),
|
||||||
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
|
"fieldname": "name",
|
||||||
_("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Amount to Bill") + ":Currency:100",
|
"fieldtype": "Link",
|
||||||
_("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120",
|
"options": "Purchase Receipt",
|
||||||
|
"width": 160
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Date"),
|
||||||
|
"fieldname": "date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Supplier"),
|
||||||
|
"fieldname": "supplier",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Supplier",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Supplier Name"),
|
||||||
|
"fieldname": "supplier_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Item Code"),
|
||||||
|
"fieldname": "item_code",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Item",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Amount"),
|
||||||
|
"fieldname": "amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 100,
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Billed Amount"),
|
||||||
|
"fieldname": "billed_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 100,
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Returned Amount"),
|
||||||
|
"fieldname": "returned_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 120,
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Pending Amount"),
|
||||||
|
"fieldname": "pending_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"width": 120,
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Item Name"),
|
||||||
|
"fieldname": "item_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Description"),
|
||||||
|
"fieldname": "description",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Project"),
|
||||||
|
"fieldname": "project",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Project",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Company"),
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Company",
|
||||||
|
"width": 120
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_args():
|
def get_args():
|
||||||
|
@ -290,11 +290,17 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
|
|||||||
dialog.show();
|
dialog.show();
|
||||||
}, __("Get Items From"));
|
}, __("Get Items From"));
|
||||||
|
|
||||||
|
// Link Material Requests
|
||||||
|
this.frm.add_custom_button(__('Link to Material Requests'),
|
||||||
|
function() {
|
||||||
|
erpnext.buying.link_to_mrs(me.frm);
|
||||||
|
}, __("Tools"));
|
||||||
|
|
||||||
// Get Suppliers
|
// Get Suppliers
|
||||||
this.frm.add_custom_button(__('Get Suppliers'),
|
this.frm.add_custom_button(__('Get Suppliers'),
|
||||||
function() {
|
function() {
|
||||||
me.get_suppliers_button(me.frm);
|
me.get_suppliers_button(me.frm);
|
||||||
});
|
}, __("Tools"));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
"suppliers",
|
"suppliers",
|
||||||
"items_section",
|
"items_section",
|
||||||
"items",
|
"items",
|
||||||
"link_to_mrs",
|
|
||||||
"supplier_response_section",
|
"supplier_response_section",
|
||||||
"salutation",
|
"salutation",
|
||||||
"subject",
|
"subject",
|
||||||
@ -118,13 +117,6 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.docstatus===0 && (doc.items && doc.items.length)",
|
|
||||||
"fieldname": "link_to_mrs",
|
|
||||||
"fieldtype": "Button",
|
|
||||||
"label": "Link to Material Requests"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"depends_on": "eval:!doc.__islocal",
|
|
||||||
"fieldname": "supplier_response_section",
|
"fieldname": "supplier_response_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Email Details"
|
"label": "Email Details"
|
||||||
@ -260,7 +252,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-11-04 22:04:29.017134",
|
"modified": "2020-11-05 22:04:29.017134",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Request for Quotation",
|
"name": "Request for Quotation",
|
||||||
|
@ -50,6 +50,12 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
|
|||||||
})
|
})
|
||||||
}, __("Get Items From"));
|
}, __("Get Items From"));
|
||||||
|
|
||||||
|
// Link Material Requests
|
||||||
|
this.frm.add_custom_button(__('Link to Material Requests'),
|
||||||
|
function() {
|
||||||
|
erpnext.buying.link_to_mrs(me.frm);
|
||||||
|
}, __("Tools"));
|
||||||
|
|
||||||
this.frm.add_custom_button(__("Request for Quotation"),
|
this.frm.add_custom_button(__("Request for Quotation"),
|
||||||
function() {
|
function() {
|
||||||
if (!me.frm.doc.supplier) {
|
if (!me.frm.doc.supplier) {
|
||||||
|
@ -35,7 +35,6 @@
|
|||||||
"ignore_pricing_rule",
|
"ignore_pricing_rule",
|
||||||
"items_section",
|
"items_section",
|
||||||
"items",
|
"items",
|
||||||
"link_to_mrs",
|
|
||||||
"pricing_rule_details",
|
"pricing_rule_details",
|
||||||
"pricing_rules",
|
"pricing_rules",
|
||||||
"section_break_22",
|
"section_break_22",
|
||||||
@ -322,12 +321,6 @@
|
|||||||
"options": "Supplier Quotation Item",
|
"options": "Supplier Quotation Item",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"depends_on": "eval:doc.docstatus===0 && (doc.items && doc.items.length)",
|
|
||||||
"fieldname": "link_to_mrs",
|
|
||||||
"fieldtype": "Button",
|
|
||||||
"label": "Link to material requests"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "pricing_rule_details",
|
"fieldname": "pricing_rule_details",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
@ -806,9 +799,10 @@
|
|||||||
],
|
],
|
||||||
"icon": "fa fa-shopping-cart",
|
"icon": "fa fa-shopping-cart",
|
||||||
"idx": 29,
|
"idx": 29,
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-10-30 13:58:33.043971",
|
"modified": "2020-12-03 15:18:29.073368",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Supplier Quotation",
|
"name": "Supplier Quotation",
|
||||||
|
@ -23,6 +23,8 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import g
|
|||||||
from erpnext.stock.get_item_details import get_item_warehouse, _get_item_tax_template, get_item_tax_map
|
from erpnext.stock.get_item_details import get_item_warehouse, _get_item_tax_template, get_item_tax_map
|
||||||
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
||||||
|
|
||||||
|
class AccountMissingError(frappe.ValidationError): pass
|
||||||
|
|
||||||
force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
|
force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
|
||||||
|
|
||||||
class AccountsController(TransactionBase):
|
class AccountsController(TransactionBase):
|
||||||
@ -735,6 +737,21 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
return self._abbr
|
return self._abbr
|
||||||
|
|
||||||
|
def raise_missing_debit_credit_account_error(self, party_type, party):
|
||||||
|
"""Raise an error if debit to/credit to account does not exist."""
|
||||||
|
db_or_cr = frappe.bold("Debit To") if self.doctype == "Sales Invoice" else frappe.bold("Credit To")
|
||||||
|
rec_or_pay = "Receivable" if self.doctype == "Sales Invoice" else "Payable"
|
||||||
|
|
||||||
|
link_to_party = frappe.utils.get_link_to_form(party_type, party)
|
||||||
|
link_to_company = frappe.utils.get_link_to_form("Company", self.company)
|
||||||
|
|
||||||
|
message = _("{0} Account not found against Customer {1}.").format(db_or_cr, frappe.bold(party) or '')
|
||||||
|
message += "<br>" + _("Please set one of the following:") + "<br>"
|
||||||
|
message += "<br><ul><li>" + _("'Account' in the Accounting section of Customer {0}").format(link_to_party) + "</li>"
|
||||||
|
message += "<li>" + _("'Default {0} Account' in Company {1}").format(rec_or_pay, link_to_company) + "</li></ul>"
|
||||||
|
|
||||||
|
frappe.throw(message, title=_("Account Missing"), exc=AccountMissingError)
|
||||||
|
|
||||||
def validate_party(self):
|
def validate_party(self):
|
||||||
party_type, party = self.get_party()
|
party_type, party = self.get_party()
|
||||||
validate_party_frozen_disabled(party_type, party)
|
validate_party_frozen_disabled(party_type, party)
|
||||||
|
@ -497,6 +497,10 @@ class BuyingController(StockController):
|
|||||||
frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx))
|
frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx))
|
||||||
d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
|
d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
|
||||||
|
|
||||||
|
if self.doctype=="Purchase Receipt" and d.meta.get_field("received_stock_qty"):
|
||||||
|
# Set Received Qty in Stock UOM
|
||||||
|
d.received_stock_qty = flt(d.received_qty) * flt(d.conversion_factor, d.precision("conversion_factor"))
|
||||||
|
|
||||||
def validate_purchase_return(self):
|
def validate_purchase_return(self):
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
if self.is_return and flt(d.rejected_qty) != 0:
|
if self.is_return and flt(d.rejected_qty) != 0:
|
||||||
|
@ -203,10 +203,37 @@ def get_already_returned_items(doc):
|
|||||||
|
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
def get_returned_qty_map_for_row(row_name, doctype):
|
||||||
|
child_doctype = doctype + " Item"
|
||||||
|
reference_field = frappe.scrub(child_doctype) if doctype == "Purchase Receipt" else "dn_detail"
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
"sum(abs(`tab{0}`.qty)) as qty".format(child_doctype),
|
||||||
|
"sum(abs(`tab{0}`.stock_qty)) as stock_qty".format(child_doctype)
|
||||||
|
]
|
||||||
|
|
||||||
|
if doctype == "Purchase Receipt":
|
||||||
|
fields += [
|
||||||
|
"sum(abs(`tab{0}`.rejected_qty)) as rejected_qty".format(child_doctype),
|
||||||
|
"sum(abs(`tab{0}`.received_qty)) as received_qty".format(child_doctype),
|
||||||
|
"sum(abs(`tab{0}`.received_stock_qty)) as received_stock_qty".format(child_doctype)
|
||||||
|
]
|
||||||
|
|
||||||
|
data = frappe.db.get_list(doctype,
|
||||||
|
fields = fields,
|
||||||
|
filters = [
|
||||||
|
[doctype, "docstatus", "=", 1],
|
||||||
|
[doctype, "is_return", "=", 1],
|
||||||
|
[child_doctype, reference_field, "=", row_name]
|
||||||
|
])
|
||||||
|
|
||||||
|
return data[0]
|
||||||
|
|
||||||
def make_return_doc(doctype, source_name, target_doc=None):
|
def make_return_doc(doctype, source_name, target_doc=None):
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
company = frappe.db.get_value("Delivery Note", source_name, "company")
|
company = frappe.db.get_value("Delivery Note", source_name, "company")
|
||||||
default_warehouse_for_sales_return = frappe.db.get_value("Company", company, "default_warehouse_for_sales_return")
|
default_warehouse_for_sales_return = frappe.db.get_value("Company", company, "default_warehouse_for_sales_return")
|
||||||
|
|
||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
doc = frappe.get_doc(target)
|
doc = frappe.get_doc(target)
|
||||||
doc.is_return = 1
|
doc.is_return = 1
|
||||||
@ -261,20 +288,25 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
|||||||
doc.run_method("calculate_taxes_and_totals")
|
doc.run_method("calculate_taxes_and_totals")
|
||||||
|
|
||||||
def update_item(source_doc, target_doc, source_parent):
|
def update_item(source_doc, target_doc, source_parent):
|
||||||
target_doc.qty = -1* source_doc.qty
|
target_doc.qty = -1 * source_doc.qty
|
||||||
|
|
||||||
if doctype == "Purchase Receipt":
|
if doctype == "Purchase Receipt":
|
||||||
target_doc.received_qty = -1* source_doc.received_qty
|
returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype)
|
||||||
target_doc.rejected_qty = -1* source_doc.rejected_qty
|
target_doc.received_qty = -1 * flt(source_doc.received_qty - (returned_qty_map.get('received_qty') or 0))
|
||||||
target_doc.qty = -1* source_doc.qty
|
target_doc.rejected_qty = -1 * flt(source_doc.rejected_qty - (returned_qty_map.get('rejected_qty') or 0))
|
||||||
target_doc.stock_qty = -1 * source_doc.stock_qty
|
target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get('qty') or 0))
|
||||||
|
|
||||||
|
target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get('stock_qty') or 0))
|
||||||
|
target_doc.received_stock_qty = -1 * flt(source_doc.received_stock_qty - (returned_qty_map.get('received_stock_qty') or 0))
|
||||||
|
|
||||||
target_doc.purchase_order = source_doc.purchase_order
|
target_doc.purchase_order = source_doc.purchase_order
|
||||||
target_doc.purchase_order_item = source_doc.purchase_order_item
|
target_doc.purchase_order_item = source_doc.purchase_order_item
|
||||||
target_doc.rejected_warehouse = source_doc.rejected_warehouse
|
target_doc.rejected_warehouse = source_doc.rejected_warehouse
|
||||||
target_doc.purchase_receipt_item = source_doc.name
|
target_doc.purchase_receipt_item = source_doc.name
|
||||||
|
|
||||||
elif doctype == "Purchase Invoice":
|
elif doctype == "Purchase Invoice":
|
||||||
target_doc.received_qty = -1* source_doc.received_qty
|
target_doc.received_qty = -1 * source_doc.received_qty
|
||||||
target_doc.rejected_qty = -1* source_doc.rejected_qty
|
target_doc.rejected_qty = -1 * source_doc.rejected_qty
|
||||||
target_doc.qty = -1* source_doc.qty
|
target_doc.qty = -1* source_doc.qty
|
||||||
target_doc.stock_qty = -1 * source_doc.stock_qty
|
target_doc.stock_qty = -1 * source_doc.stock_qty
|
||||||
target_doc.purchase_order = source_doc.purchase_order
|
target_doc.purchase_order = source_doc.purchase_order
|
||||||
@ -285,6 +317,10 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
|||||||
target_doc.purchase_invoice_item = source_doc.name
|
target_doc.purchase_invoice_item = source_doc.name
|
||||||
|
|
||||||
elif doctype == "Delivery Note":
|
elif doctype == "Delivery Note":
|
||||||
|
returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype)
|
||||||
|
target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get('qty') or 0))
|
||||||
|
target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get('stock_qty') or 0))
|
||||||
|
|
||||||
target_doc.against_sales_order = source_doc.against_sales_order
|
target_doc.against_sales_order = source_doc.against_sales_order
|
||||||
target_doc.against_sales_invoice = source_doc.against_sales_invoice
|
target_doc.against_sales_invoice = source_doc.against_sales_invoice
|
||||||
target_doc.so_detail = source_doc.so_detail
|
target_doc.so_detail = source_doc.so_detail
|
||||||
|
@ -58,6 +58,7 @@ status_map = {
|
|||||||
"Delivery Note": [
|
"Delivery Note": [
|
||||||
["Draft", None],
|
["Draft", None],
|
||||||
["To Bill", "eval:self.per_billed < 100 and self.docstatus == 1"],
|
["To Bill", "eval:self.per_billed < 100 and self.docstatus == 1"],
|
||||||
|
["Return Issued", "eval:self.per_returned == 100 and self.docstatus == 1"],
|
||||||
["Completed", "eval:self.per_billed == 100 and self.docstatus == 1"],
|
["Completed", "eval:self.per_billed == 100 and self.docstatus == 1"],
|
||||||
["Cancelled", "eval:self.docstatus==2"],
|
["Cancelled", "eval:self.docstatus==2"],
|
||||||
["Closed", "eval:self.status=='Closed'"],
|
["Closed", "eval:self.status=='Closed'"],
|
||||||
@ -65,6 +66,7 @@ status_map = {
|
|||||||
"Purchase Receipt": [
|
"Purchase Receipt": [
|
||||||
["Draft", None],
|
["Draft", None],
|
||||||
["To Bill", "eval:self.per_billed < 100 and self.docstatus == 1"],
|
["To Bill", "eval:self.per_billed < 100 and self.docstatus == 1"],
|
||||||
|
["Return Issued", "eval:self.per_returned == 100 and self.docstatus == 1"],
|
||||||
["Completed", "eval:self.per_billed == 100 and self.docstatus == 1"],
|
["Completed", "eval:self.per_billed == 100 and self.docstatus == 1"],
|
||||||
["Cancelled", "eval:self.docstatus==2"],
|
["Cancelled", "eval:self.docstatus==2"],
|
||||||
["Closed", "eval:self.status=='Closed'"],
|
["Closed", "eval:self.status=='Closed'"],
|
||||||
@ -232,7 +234,7 @@ class StatusUpdater(Document):
|
|||||||
|
|
||||||
self._update_children(args, update_modified)
|
self._update_children(args, update_modified)
|
||||||
|
|
||||||
if "percent_join_field" in args:
|
if "percent_join_field" in args or "percent_join_field_parent" in args:
|
||||||
self._update_percent_field_in_targets(args, update_modified)
|
self._update_percent_field_in_targets(args, update_modified)
|
||||||
|
|
||||||
def _update_children(self, args, update_modified):
|
def _update_children(self, args, update_modified):
|
||||||
@ -272,13 +274,19 @@ class StatusUpdater(Document):
|
|||||||
|
|
||||||
def _update_percent_field_in_targets(self, args, update_modified=True):
|
def _update_percent_field_in_targets(self, args, update_modified=True):
|
||||||
"""Update percent field in parent transaction"""
|
"""Update percent field in parent transaction"""
|
||||||
distinct_transactions = set([d.get(args['percent_join_field'])
|
if args.get('percent_join_field_parent'):
|
||||||
for d in self.get_all_children(args['source_dt'])])
|
# if reference to target doc where % is to be updated, is
|
||||||
|
# in source doc's parent form, consider percent_join_field_parent
|
||||||
|
args['name'] = self.get(args['percent_join_field_parent'])
|
||||||
|
self._update_percent_field(args, update_modified)
|
||||||
|
else:
|
||||||
|
distinct_transactions = set([d.get(args['percent_join_field'])
|
||||||
|
for d in self.get_all_children(args['source_dt'])])
|
||||||
|
|
||||||
for name in distinct_transactions:
|
for name in distinct_transactions:
|
||||||
if name:
|
if name:
|
||||||
args['name'] = name
|
args['name'] = name
|
||||||
self._update_percent_field(args, update_modified)
|
self._update_percent_field(args, update_modified)
|
||||||
|
|
||||||
def _update_percent_field(self, args, update_modified=True):
|
def _update_percent_field(self, args, update_modified=True):
|
||||||
"""Update percent field in parent transaction"""
|
"""Update percent field in parent transaction"""
|
||||||
|
@ -340,11 +340,15 @@ class StockController(AccountsController):
|
|||||||
validate_warehouse_company(w, self.company)
|
validate_warehouse_company(w, self.company)
|
||||||
|
|
||||||
def update_billing_percentage(self, update_modified=True):
|
def update_billing_percentage(self, update_modified=True):
|
||||||
|
target_ref_field = "amount"
|
||||||
|
if self.doctype == "Delivery Note":
|
||||||
|
target_ref_field = "amount - (returned_qty * rate)"
|
||||||
|
|
||||||
self._update_percent_field({
|
self._update_percent_field({
|
||||||
"target_dt": self.doctype + " Item",
|
"target_dt": self.doctype + " Item",
|
||||||
"target_parent_dt": self.doctype,
|
"target_parent_dt": self.doctype,
|
||||||
"target_parent_field": "per_billed",
|
"target_parent_field": "per_billed",
|
||||||
"target_ref_field": "amount",
|
"target_ref_field": target_ref_field,
|
||||||
"target_field": "billed_amt",
|
"target_field": "billed_amt",
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
}, update_modified)
|
}, update_modified)
|
||||||
|
@ -691,6 +691,7 @@ erpnext.patches.v13_0.update_old_loans
|
|||||||
erpnext.patches.v12_0.set_serial_no_status #2020-05-21
|
erpnext.patches.v12_0.set_serial_no_status #2020-05-21
|
||||||
erpnext.patches.v12_0.update_price_list_currency_in_bom
|
erpnext.patches.v12_0.update_price_list_currency_in_bom
|
||||||
execute:frappe.reload_doctype('Dashboard')
|
execute:frappe.reload_doctype('Dashboard')
|
||||||
|
execute:frappe.reload_doc('desk', 'doctype', 'number_card_link')
|
||||||
execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts')
|
execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts')
|
||||||
erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo
|
erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo
|
||||||
erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes #2020-05-25
|
erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes #2020-05-25
|
||||||
@ -738,3 +739,4 @@ erpnext.patches.v13_0.update_custom_fields_for_shopify
|
|||||||
execute:frappe.delete_doc("Report", "Quoted Item Comparison")
|
execute:frappe.delete_doc("Report", "Quoted Item Comparison")
|
||||||
erpnext.patches.v13_0.updates_for_multi_currency_payroll
|
erpnext.patches.v13_0.updates_for_multi_currency_payroll
|
||||||
erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy
|
erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy
|
||||||
|
erpnext.patches.v13_0.update_returned_qty_in_pr_dn
|
||||||
|
@ -52,6 +52,8 @@ def create_assignment(employee, leave_policy, leave_period=None, allocation_exis
|
|||||||
if leave_period:
|
if leave_period:
|
||||||
filters["leave_period"] = leave_period
|
filters["leave_period"] = leave_period
|
||||||
|
|
||||||
|
frappe.reload_doc('hr', 'doctype', 'leave_policy_assignment')
|
||||||
|
|
||||||
if not frappe.db.exists("Leave Policy Assignment" , filters):
|
if not frappe.db.exists("Leave Policy Assignment" , filters):
|
||||||
lpa = frappe.new_doc("Leave Policy Assignment")
|
lpa = frappe.new_doc("Leave Policy Assignment")
|
||||||
lpa.employee = employee
|
lpa.employee = employee
|
||||||
|
27
erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py
Normal file
27
erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Copyright (c) 2019, Frappe and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'purchase_receipt')
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'purchase_receipt_item')
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'delivery_note')
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'delivery_note_item')
|
||||||
|
|
||||||
|
def update_from_return_docs(doctype):
|
||||||
|
for return_doc in frappe.get_all(doctype, filters={'is_return' : 1, 'docstatus' : 1}):
|
||||||
|
# Update original receipt/delivery document from return
|
||||||
|
return_doc = frappe.get_cached_doc(doctype, return_doc.name)
|
||||||
|
return_doc.update_prevdoc_status()
|
||||||
|
return_against = frappe.get_doc(doctype, return_doc.return_against)
|
||||||
|
return_against.update_billing_status()
|
||||||
|
|
||||||
|
# Set received qty in stock uom in PR, as returned qty is checked against it
|
||||||
|
frappe.db.sql(""" update `tabPurchase Receipt Item`
|
||||||
|
set received_stock_qty = received_qty * conversion_factor
|
||||||
|
where docstatus = 1 """)
|
||||||
|
|
||||||
|
for doctype in ('Purchase Receipt', 'Delivery Note'):
|
||||||
|
update_from_return_docs(doctype)
|
@ -7,6 +7,10 @@ import frappe
|
|||||||
def execute():
|
def execute():
|
||||||
parent_list = []
|
parent_list = []
|
||||||
count = 0
|
count = 0
|
||||||
|
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'purchase_receipt')
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'purchase_receipt_item')
|
||||||
|
|
||||||
for data in frappe.db.sql("""
|
for data in frappe.db.sql("""
|
||||||
select
|
select
|
||||||
`tabPurchase Receipt Item`.purchase_order, `tabPurchase Receipt Item`.name,
|
`tabPurchase Receipt Item`.purchase_order, `tabPurchase Receipt Item`.name,
|
||||||
|
@ -189,6 +189,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
|
|
||||||
frappe.model.round_floats_in(item, ["qty", "received_qty"]);
|
frappe.model.round_floats_in(item, ["qty", "received_qty"]);
|
||||||
item.rejected_qty = flt(item.received_qty - item.qty, precision("rejected_qty", item));
|
item.rejected_qty = flt(item.received_qty - item.qty, precision("rejected_qty", item));
|
||||||
|
item.received_stock_qty = flt(item.conversion_factor, precision("conversion_factor", item)) * flt(item.received_qty);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._super(doc, cdt, cdn);
|
this._super(doc, cdt, cdn);
|
||||||
@ -293,69 +294,6 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
this.get_terms();
|
this.get_terms();
|
||||||
},
|
},
|
||||||
|
|
||||||
link_to_mrs: function() {
|
|
||||||
var my_items = [];
|
|
||||||
for (var i in cur_frm.doc.items) {
|
|
||||||
if(!cur_frm.doc.items[i].material_request){
|
|
||||||
my_items.push(cur_frm.doc.items[i].item_code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
frappe.call({
|
|
||||||
method: "erpnext.buying.utils.get_linked_material_requests",
|
|
||||||
args:{
|
|
||||||
items: my_items
|
|
||||||
},
|
|
||||||
callback: function(r) {
|
|
||||||
if(!r.message || r.message.length == 0) {
|
|
||||||
frappe.throw(__("No pending Material Requests found to link for the given items."))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var i = 0;
|
|
||||||
var item_length = cur_frm.doc.items.length;
|
|
||||||
while (i < item_length) {
|
|
||||||
var qty = cur_frm.doc.items[i].qty;
|
|
||||||
(r.message[0] || []).forEach(function(d) {
|
|
||||||
if (d.qty > 0 && qty > 0 && cur_frm.doc.items[i].item_code == d.item_code && !cur_frm.doc.items[i].material_request_item)
|
|
||||||
{
|
|
||||||
cur_frm.doc.items[i].material_request = d.mr_name;
|
|
||||||
cur_frm.doc.items[i].material_request_item = d.mr_item;
|
|
||||||
var my_qty = Math.min(qty, d.qty);
|
|
||||||
qty = qty - my_qty;
|
|
||||||
d.qty = d.qty - my_qty;
|
|
||||||
cur_frm.doc.items[i].stock_qty = my_qty*cur_frm.doc.items[i].conversion_factor;
|
|
||||||
cur_frm.doc.items[i].qty = my_qty;
|
|
||||||
|
|
||||||
frappe.msgprint("Assigning " + d.mr_name + " to " + d.item_code + " (row " + cur_frm.doc.items[i].idx + ")");
|
|
||||||
if (qty > 0)
|
|
||||||
{
|
|
||||||
frappe.msgprint("Splitting " + qty + " units of " + d.item_code);
|
|
||||||
var newrow = frappe.model.add_child(cur_frm.doc, cur_frm.doc.items[i].doctype, "items");
|
|
||||||
item_length++;
|
|
||||||
|
|
||||||
for (var key in cur_frm.doc.items[i])
|
|
||||||
{
|
|
||||||
newrow[key] = cur_frm.doc.items[i][key];
|
|
||||||
}
|
|
||||||
|
|
||||||
newrow.idx = item_length;
|
|
||||||
newrow["stock_qty"] = newrow.conversion_factor*qty;
|
|
||||||
newrow["qty"] = qty;
|
|
||||||
|
|
||||||
newrow["material_request"] = "";
|
|
||||||
newrow["material_request_item"] = "";
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
refresh_field("items");
|
|
||||||
//cur_frm.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
update_auto_repeat_reference: function(doc) {
|
update_auto_repeat_reference: function(doc) {
|
||||||
if (doc.auto_repeat) {
|
if (doc.auto_repeat) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
@ -421,6 +359,62 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
|
|
||||||
cur_frm.add_fetch('project', 'cost_center', 'cost_center');
|
cur_frm.add_fetch('project', 'cost_center', 'cost_center');
|
||||||
|
|
||||||
|
erpnext.buying.link_to_mrs = function(frm) {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.buying.utils.get_linked_material_requests",
|
||||||
|
args:{
|
||||||
|
items: frm.doc.items.map((item) => item.item_code)
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if (!r.message || r.message.length == 0) {
|
||||||
|
frappe.throw({
|
||||||
|
message: __("No pending Material Requests found to link for the given items."),
|
||||||
|
title: __("Note")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var item_length = frm.doc.items.length;
|
||||||
|
for (let item of frm.doc.items) {
|
||||||
|
var qty = item.qty;
|
||||||
|
(r.message[0] || []).forEach(function(d) {
|
||||||
|
if (d.qty > 0 && qty > 0 && item.item_code == d.item_code && !item.material_request_item)
|
||||||
|
{
|
||||||
|
item.material_request = d.mr_name;
|
||||||
|
item.material_request_item = d.mr_item;
|
||||||
|
var my_qty = Math.min(qty, d.qty);
|
||||||
|
qty = qty - my_qty;
|
||||||
|
d.qty = d.qty - my_qty;
|
||||||
|
item.stock_qty = my_qty*item.conversion_factor;
|
||||||
|
item.qty = my_qty;
|
||||||
|
|
||||||
|
frappe.msgprint("Assigning " + d.mr_name + " to " + d.item_code + " (row " + item.idx + ")");
|
||||||
|
if (qty > 0)
|
||||||
|
{
|
||||||
|
frappe.msgprint("Splitting " + qty + " units of " + d.item_code);
|
||||||
|
var newrow = frappe.model.add_child(frm.doc, item.doctype, "items");
|
||||||
|
item_length++;
|
||||||
|
|
||||||
|
for (var key in item)
|
||||||
|
{
|
||||||
|
newrow[key] = item[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
newrow.idx = item_length;
|
||||||
|
newrow["stock_qty"] = newrow.conversion_factor*qty;
|
||||||
|
newrow["qty"] = qty;
|
||||||
|
|
||||||
|
newrow["material_request"] = "";
|
||||||
|
newrow["material_request_item"] = "";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
refresh_field("items");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
erpnext.buying.get_default_bom = function(frm) {
|
erpnext.buying.get_default_bom = function(frm) {
|
||||||
$.each(frm.doc["items"] || [], function(i, d) {
|
$.each(frm.doc["items"] || [], function(i, d) {
|
||||||
if (d.item_code && d.bom === "") {
|
if (d.item_code && d.bom === "") {
|
||||||
|
@ -192,19 +192,20 @@ class GSTR3BReport(Document):
|
|||||||
for d in self.report_dict["itc_elg"]["itc_avl"]:
|
for d in self.report_dict["itc_elg"]["itc_avl"]:
|
||||||
|
|
||||||
itc_type = itc_type_map.get(d["ty"])
|
itc_type = itc_type_map.get(d["ty"])
|
||||||
gst_category = ["Registered Regular"]
|
|
||||||
|
|
||||||
if d["ty"] == 'ISRC':
|
if d["ty"] == 'ISRC':
|
||||||
reverse_charge = "Y"
|
reverse_charge = ["Y"]
|
||||||
itc_type = 'All Other ITC'
|
itc_type = 'All Other ITC'
|
||||||
gst_category = ['Unregistered', 'Overseas']
|
gst_category = ['Unregistered', 'Overseas']
|
||||||
else:
|
else:
|
||||||
reverse_charge = "N"
|
gst_category = ['Unregistered', 'Overseas', 'Registered Regular']
|
||||||
|
reverse_charge = ["N", "Y"]
|
||||||
|
|
||||||
for account_head in self.account_heads:
|
for account_head in self.account_heads:
|
||||||
for category in gst_category:
|
for category in gst_category:
|
||||||
for key in [['iamt', 'igst_account'], ['camt', 'cgst_account'], ['samt', 'sgst_account'], ['csamt', 'cess_account']]:
|
for charge_type in reverse_charge:
|
||||||
d[key[0]] += flt(itc_details.get((category, itc_type, reverse_charge, account_head.get(key[1])), {}).get("amount"), 2)
|
for key in [['iamt', 'igst_account'], ['camt', 'cgst_account'], ['samt', 'sgst_account'], ['csamt', 'cess_account']]:
|
||||||
|
d[key[0]] += flt(itc_details.get((category, itc_type, charge_type, account_head.get(key[1])), {}).get("amount"), 2)
|
||||||
|
|
||||||
for key in ['iamt', 'camt', 'samt', 'csamt']:
|
for key in ['iamt', 'camt', 'samt', 'csamt']:
|
||||||
net_itc[key] += flt(d[key], 2)
|
net_itc[key] += flt(d[key], 2)
|
||||||
@ -264,7 +265,8 @@ class GSTR3BReport(Document):
|
|||||||
|
|
||||||
def get_itc_details(self):
|
def get_itc_details(self):
|
||||||
itc_amount = frappe.db.sql("""
|
itc_amount = frappe.db.sql("""
|
||||||
select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge
|
select s.gst_category, sum(t.base_tax_amount_after_discount_amount) as tax_amount,
|
||||||
|
t.account_head, s.eligibility_for_itc, s.reverse_charge
|
||||||
from `tabPurchase Invoice` s , `tabPurchase Taxes and Charges` t
|
from `tabPurchase Invoice` s , `tabPurchase Taxes and Charges` t
|
||||||
where s.docstatus = 1 and t.parent = s.name
|
where s.docstatus = 1 and t.parent = s.name
|
||||||
and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s
|
and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s
|
||||||
@ -387,7 +389,7 @@ class GSTR3BReport(Document):
|
|||||||
tax_template = 'Purchase Taxes and Charges'
|
tax_template = 'Purchase Taxes and Charges'
|
||||||
|
|
||||||
tax_amounts = frappe.db.sql("""
|
tax_amounts = frappe.db.sql("""
|
||||||
select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head
|
select s.gst_category, sum(t.base_tax_amount_after_discount_amount) as tax_amount, t.account_head
|
||||||
from `tab{doctype}` s , `tab{template}` t
|
from `tab{doctype}` s , `tab{template}` t
|
||||||
where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s
|
where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s
|
||||||
and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s
|
and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s
|
||||||
|
@ -19,6 +19,7 @@ erpnext.setup_auto_gst_taxation = (doctype) => {
|
|||||||
'shipping_address': frm.doc.shipping_address || '',
|
'shipping_address': frm.doc.shipping_address || '',
|
||||||
'shipping_address_name': frm.doc.shipping_address_name || '',
|
'shipping_address_name': frm.doc.shipping_address_name || '',
|
||||||
'customer_address': frm.doc.customer_address || '',
|
'customer_address': frm.doc.customer_address || '',
|
||||||
|
'supplier_address': frm.doc.supplier_address,
|
||||||
'customer': frm.doc.customer,
|
'customer': frm.doc.customer,
|
||||||
'supplier': frm.doc.supplier,
|
'supplier': frm.doc.supplier,
|
||||||
'supplier_gstin': frm.doc.supplier_gstin,
|
'supplier_gstin': frm.doc.supplier_gstin,
|
||||||
|
@ -12,6 +12,7 @@ from erpnext.regional.india import number_state_mapping
|
|||||||
from six import string_types
|
from six import string_types
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
from erpnext.accounts.utils import get_account_currency
|
from erpnext.accounts.utils import get_account_currency
|
||||||
|
from frappe.model.utils import get_fetch_values
|
||||||
|
|
||||||
def validate_gstin_for_india(doc, method):
|
def validate_gstin_for_india(doc, method):
|
||||||
if hasattr(doc, 'gst_state') and doc.gst_state:
|
if hasattr(doc, 'gst_state') and doc.gst_state:
|
||||||
@ -161,6 +162,8 @@ def get_regional_address_details(party_details, doctype, company):
|
|||||||
party_details = json.loads(party_details)
|
party_details = json.loads(party_details)
|
||||||
party_details = frappe._dict(party_details)
|
party_details = frappe._dict(party_details)
|
||||||
|
|
||||||
|
update_party_details(party_details, doctype)
|
||||||
|
|
||||||
party_details.place_of_supply = get_place_of_supply(party_details, doctype)
|
party_details.place_of_supply = get_place_of_supply(party_details, doctype)
|
||||||
|
|
||||||
if is_internal_transfer(party_details, doctype):
|
if is_internal_transfer(party_details, doctype):
|
||||||
@ -209,6 +212,11 @@ def get_regional_address_details(party_details, doctype, company):
|
|||||||
|
|
||||||
return party_details
|
return party_details
|
||||||
|
|
||||||
|
def update_party_details(party_details, doctype):
|
||||||
|
for address_field in ['shipping_address', 'company_address', 'supplier_address', 'shipping_address_name', 'customer_address']:
|
||||||
|
if party_details.get(address_field):
|
||||||
|
party_details.update(get_fetch_values(doctype, address_field, party_details.get(address_field)))
|
||||||
|
|
||||||
def is_internal_transfer(party_details, doctype):
|
def is_internal_transfer(party_details, doctype):
|
||||||
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
|
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
|
||||||
destination_gstin = party_details.company_gstin
|
destination_gstin = party_details.company_gstin
|
||||||
|
@ -133,6 +133,7 @@
|
|||||||
"per_installed",
|
"per_installed",
|
||||||
"installation_status",
|
"installation_status",
|
||||||
"column_break_89",
|
"column_break_89",
|
||||||
|
"per_returned",
|
||||||
"excise_page",
|
"excise_page",
|
||||||
"instructions",
|
"instructions",
|
||||||
"subscription_section",
|
"subscription_section",
|
||||||
@ -1099,7 +1100,7 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "status",
|
"oldfieldname": "status",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "\nDraft\nTo Bill\nCompleted\nCancelled\nClosed",
|
"options": "\nDraft\nTo Bill\nCompleted\nReturn Issued\nCancelled\nClosed",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_width": "150px",
|
"print_width": "150px",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
@ -1251,13 +1252,22 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Inter Company Reference",
|
"label": "Inter Company Reference",
|
||||||
"options": "Purchase Receipt"
|
"options": "Purchase Receipt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:!doc.__islocal",
|
||||||
|
"fieldname": "per_returned",
|
||||||
|
"fieldtype": "Percent",
|
||||||
|
"label": "% Returned",
|
||||||
|
"no_copy": 1,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-truck",
|
"icon": "fa fa-truck",
|
||||||
"idx": 146,
|
"idx": 146,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-11-11 14:57:16.388139",
|
"modified": "2020-11-30 12:54:45.407289",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Delivery Note",
|
"name": "Delivery Note",
|
||||||
|
@ -55,7 +55,7 @@ class DeliveryNote(SellingController):
|
|||||||
'no_allowance': 1
|
'no_allowance': 1
|
||||||
}]
|
}]
|
||||||
if cint(self.is_return):
|
if cint(self.is_return):
|
||||||
self.status_updater.append({
|
self.status_updater.extend([{
|
||||||
'source_dt': 'Delivery Note Item',
|
'source_dt': 'Delivery Note Item',
|
||||||
'target_dt': 'Sales Order Item',
|
'target_dt': 'Sales Order Item',
|
||||||
'join_field': 'so_detail',
|
'join_field': 'so_detail',
|
||||||
@ -69,7 +69,19 @@ class DeliveryNote(SellingController):
|
|||||||
where name=`tabDelivery Note Item`.parent and is_return=1)""",
|
where name=`tabDelivery Note Item`.parent and is_return=1)""",
|
||||||
'second_source_extra_cond': """ and exists (select name from `tabSales Invoice`
|
'second_source_extra_cond': """ and exists (select name from `tabSales Invoice`
|
||||||
where name=`tabSales Invoice Item`.parent and is_return=1 and update_stock=1)"""
|
where name=`tabSales Invoice Item`.parent and is_return=1 and update_stock=1)"""
|
||||||
})
|
},
|
||||||
|
{
|
||||||
|
'source_dt': 'Delivery Note Item',
|
||||||
|
'target_dt': 'Delivery Note Item',
|
||||||
|
'join_field': 'dn_detail',
|
||||||
|
'target_field': 'returned_qty',
|
||||||
|
'target_parent_dt': 'Delivery Note',
|
||||||
|
'target_parent_field': 'per_returned',
|
||||||
|
'target_ref_field': 'stock_qty',
|
||||||
|
'source_field': '-1 * stock_qty',
|
||||||
|
'percent_join_field_parent': 'return_against'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
def before_print(self):
|
def before_print(self):
|
||||||
def toggle_print_hide(meta, fieldname):
|
def toggle_print_hide(meta, fieldname):
|
||||||
|
@ -6,9 +6,11 @@ frappe.listview_settings['Delivery Note'] = {
|
|||||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||||
} else if (doc.status === "Closed") {
|
} else if (doc.status === "Closed") {
|
||||||
return [__("Closed"), "green", "status,=,Closed"];
|
return [__("Closed"), "green", "status,=,Closed"];
|
||||||
|
} else if (flt(doc.per_returned, 2) === 100) {
|
||||||
|
return [__("Return Issued"), "grey", "per_returned,=,100"];
|
||||||
} else if (flt(doc.per_billed, 2) < 100) {
|
} else if (flt(doc.per_billed, 2) < 100) {
|
||||||
return [__("To Bill"), "orange", "per_billed,<,100"];
|
return [__("To Bill"), "orange", "per_billed,<,100"];
|
||||||
} else if (flt(doc.per_billed, 2) == 100) {
|
} else if (flt(doc.per_billed, 2) === 100) {
|
||||||
return [__("Completed"), "green", "per_billed,=,100"];
|
return [__("Completed"), "green", "per_billed,=,100"];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -206,7 +206,7 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
for field, value in field_values.items():
|
for field, value in field_values.items():
|
||||||
self.assertEqual(cstr(serial_no.get(field)), value)
|
self.assertEqual(cstr(serial_no.get(field)), value)
|
||||||
|
|
||||||
def test_sales_return_for_non_bundled_items(self):
|
def test_sales_return_for_non_bundled_items_partial(self):
|
||||||
company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
|
company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
|
||||||
|
|
||||||
make_stock_entry(item_code="_Test Item", target="Stores - TCP1", qty=50, basic_rate=100)
|
make_stock_entry(item_code="_Test Item", target="Stores - TCP1", qty=50, basic_rate=100)
|
||||||
@ -225,7 +225,10 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
|
|
||||||
# return entry
|
# return entry
|
||||||
dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-2, rate=500,
|
dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-2, rate=500,
|
||||||
company=company, warehouse="Stores - TCP1", expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1")
|
company=company, warehouse="Stores - TCP1", expense_account="Cost of Goods Sold - TCP1",
|
||||||
|
cost_center="Main - TCP1", do_not_submit=1)
|
||||||
|
dn1.items[0].dn_detail = dn.items[0].name
|
||||||
|
dn1.submit()
|
||||||
|
|
||||||
actual_qty_2 = get_qty_after_transaction(warehouse="Stores - TCP1")
|
actual_qty_2 = get_qty_after_transaction(warehouse="Stores - TCP1")
|
||||||
|
|
||||||
@ -243,6 +246,70 @@ class TestDeliveryNote(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(gle_warehouse_amount, stock_value_difference)
|
self.assertEqual(gle_warehouse_amount, stock_value_difference)
|
||||||
|
|
||||||
|
# hack because new_doc isn't considering is_return portion of status_updater
|
||||||
|
returned = frappe.get_doc("Delivery Note", dn1.name)
|
||||||
|
returned.update_prevdoc_status()
|
||||||
|
dn.load_from_db()
|
||||||
|
|
||||||
|
# Check if Original DN updated
|
||||||
|
self.assertEqual(dn.items[0].returned_qty, 2)
|
||||||
|
self.assertEqual(dn.per_returned, 40)
|
||||||
|
|
||||||
|
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||||
|
return_dn_2 = make_return_doc("Delivery Note", dn.name)
|
||||||
|
|
||||||
|
# Check if unreturned amount is mapped in 2nd return
|
||||||
|
self.assertEqual(return_dn_2.items[0].qty, -3)
|
||||||
|
|
||||||
|
si = make_sales_invoice(dn.name)
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
self.assertEqual(si.items[0].qty, 3)
|
||||||
|
|
||||||
|
dn.load_from_db()
|
||||||
|
# DN should be completed on billing all unreturned amount
|
||||||
|
self.assertEqual(dn.items[0].billed_amt, 1500)
|
||||||
|
self.assertEqual(dn.per_billed, 100)
|
||||||
|
self.assertEqual(dn.status, 'Completed')
|
||||||
|
|
||||||
|
si.load_from_db()
|
||||||
|
si.cancel()
|
||||||
|
|
||||||
|
dn.load_from_db()
|
||||||
|
self.assertEqual(dn.per_billed, 0)
|
||||||
|
|
||||||
|
dn1.cancel()
|
||||||
|
dn.cancel()
|
||||||
|
|
||||||
|
def test_sales_return_for_non_bundled_items_full(self):
|
||||||
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
|
||||||
|
company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
|
||||||
|
|
||||||
|
make_item("Box", {'is_stock_item': 1})
|
||||||
|
|
||||||
|
make_stock_entry(item_code="Box", target="Stores - TCP1", qty=10, basic_rate=100)
|
||||||
|
|
||||||
|
dn = create_delivery_note(item_code="Box", qty=5, rate=500, warehouse="Stores - TCP1", company=company,
|
||||||
|
expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1")
|
||||||
|
|
||||||
|
#return entry
|
||||||
|
dn1 = create_delivery_note(item_code="Box", is_return=1, return_against=dn.name, qty=-5, rate=500,
|
||||||
|
company=company, warehouse="Stores - TCP1", expense_account="Cost of Goods Sold - TCP1",
|
||||||
|
cost_center="Main - TCP1", do_not_submit=1)
|
||||||
|
dn1.items[0].dn_detail = dn.items[0].name
|
||||||
|
dn1.submit()
|
||||||
|
|
||||||
|
# hack because new_doc isn't considering is_return portion of status_updater
|
||||||
|
returned = frappe.get_doc("Delivery Note", dn1.name)
|
||||||
|
returned.update_prevdoc_status()
|
||||||
|
dn.load_from_db()
|
||||||
|
|
||||||
|
# Check if Original DN updated
|
||||||
|
self.assertEqual(dn.items[0].returned_qty, 5)
|
||||||
|
self.assertEqual(dn.per_returned, 100)
|
||||||
|
self.assertEqual(dn.status, 'Return Issued')
|
||||||
|
|
||||||
def test_return_single_item_from_bundled_items(self):
|
def test_return_single_item_from_bundled_items(self):
|
||||||
company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
|
company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"creation": "2013-04-22 13:15:44",
|
"creation": "2013-04-22 13:15:44",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@ -24,7 +25,10 @@
|
|||||||
"col_break2",
|
"col_break2",
|
||||||
"uom",
|
"uom",
|
||||||
"conversion_factor",
|
"conversion_factor",
|
||||||
|
"stock_qty_sec_break",
|
||||||
"stock_qty",
|
"stock_qty",
|
||||||
|
"stock_qty_col_break",
|
||||||
|
"returned_qty",
|
||||||
"section_break_17",
|
"section_break_17",
|
||||||
"price_list_rate",
|
"price_list_rate",
|
||||||
"base_price_list_rate",
|
"base_price_list_rate",
|
||||||
@ -211,7 +215,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "stock_qty",
|
"fieldname": "stock_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Qty as per Stock UOM",
|
"label": "Qty in Stock UOM",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
@ -715,12 +719,29 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "stock_qty_sec_break",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "stock_qty_col_break",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "returned_qty",
|
||||||
|
"fieldname": "returned_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Returned Qty in Stock UOM",
|
||||||
|
"no_copy": 1,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-07-20 12:25:06.177894",
|
"modified": "2020-07-31 20:12:43.054342",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Delivery Note Item",
|
"name": "Delivery Note Item",
|
||||||
|
@ -111,6 +111,7 @@
|
|||||||
"range",
|
"range",
|
||||||
"column_break4",
|
"column_break4",
|
||||||
"per_billed",
|
"per_billed",
|
||||||
|
"per_returned",
|
||||||
"is_internal_supplier",
|
"is_internal_supplier",
|
||||||
"inter_company_reference",
|
"inter_company_reference",
|
||||||
"subscription_detail",
|
"subscription_detail",
|
||||||
@ -895,7 +896,7 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "status",
|
"oldfieldname": "status",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "\nDraft\nTo Bill\nCompleted\nCancelled\nClosed",
|
"options": "\nDraft\nTo Bill\nCompleted\nReturn Issued\nCancelled\nClosed",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_width": "150px",
|
"print_width": "150px",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
@ -1104,13 +1105,22 @@
|
|||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"label": "Billing Address",
|
"label": "Billing Address",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:!doc.__islocal",
|
||||||
|
"fieldname": "per_returned",
|
||||||
|
"fieldtype": "Percent",
|
||||||
|
"label": "% Returned",
|
||||||
|
"no_copy": 1,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-truck",
|
"icon": "fa fa-truck",
|
||||||
"idx": 261,
|
"idx": 261,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-10-30 14:00:08.347534",
|
"modified": "2020-11-30 12:54:23.278500",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Purchase Receipt",
|
"name": "Purchase Receipt",
|
||||||
|
@ -55,20 +55,33 @@ class PurchaseReceipt(BuyingController):
|
|||||||
'percent_join_field': 'material_request'
|
'percent_join_field': 'material_request'
|
||||||
}]
|
}]
|
||||||
if cint(self.is_return):
|
if cint(self.is_return):
|
||||||
self.status_updater.append({
|
self.status_updater.extend([
|
||||||
'source_dt': 'Purchase Receipt Item',
|
{
|
||||||
'target_dt': 'Purchase Order Item',
|
'source_dt': 'Purchase Receipt Item',
|
||||||
'join_field': 'purchase_order_item',
|
'target_dt': 'Purchase Order Item',
|
||||||
'target_field': 'returned_qty',
|
'join_field': 'purchase_order_item',
|
||||||
'source_field': '-1 * qty',
|
'target_field': 'returned_qty',
|
||||||
'second_source_dt': 'Purchase Invoice Item',
|
'source_field': '-1 * qty',
|
||||||
'second_source_field': '-1 * qty',
|
'second_source_dt': 'Purchase Invoice Item',
|
||||||
'second_join_field': 'po_detail',
|
'second_source_field': '-1 * qty',
|
||||||
'extra_cond': """ and exists (select name from `tabPurchase Receipt`
|
'second_join_field': 'po_detail',
|
||||||
where name=`tabPurchase Receipt Item`.parent and is_return=1)""",
|
'extra_cond': """ and exists (select name from `tabPurchase Receipt`
|
||||||
'second_source_extra_cond': """ and exists (select name from `tabPurchase Invoice`
|
where name=`tabPurchase Receipt Item`.parent and is_return=1)""",
|
||||||
where name=`tabPurchase Invoice Item`.parent and is_return=1 and update_stock=1)"""
|
'second_source_extra_cond': """ and exists (select name from `tabPurchase Invoice`
|
||||||
})
|
where name=`tabPurchase Invoice Item`.parent and is_return=1 and update_stock=1)"""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'source_dt': 'Purchase Receipt Item',
|
||||||
|
'target_dt': 'Purchase Receipt Item',
|
||||||
|
'join_field': 'purchase_receipt_item',
|
||||||
|
'target_field': 'returned_qty',
|
||||||
|
'target_parent_dt': 'Purchase Receipt',
|
||||||
|
'target_parent_field': 'per_returned',
|
||||||
|
'target_ref_field': 'received_stock_qty',
|
||||||
|
'source_field': '-1 * received_stock_qty',
|
||||||
|
'percent_join_field_parent': 'return_against'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_posting_time()
|
self.validate_posting_time()
|
||||||
@ -478,7 +491,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(valuation_rate))
|
frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(valuation_rate))
|
||||||
|
|
||||||
def update_status(self, status):
|
def update_status(self, status):
|
||||||
self.set_status(update=True, status = status)
|
self.set_status(update=True, status=status)
|
||||||
self.notify_update()
|
self.notify_update()
|
||||||
clear_doctype_notifications(self)
|
clear_doctype_notifications(self)
|
||||||
|
|
||||||
@ -490,7 +503,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
|
|
||||||
for pr in set(updated_pr):
|
for pr in set(updated_pr):
|
||||||
pr_doc = self if (pr == self.name) else frappe.get_doc("Purchase Receipt", pr)
|
pr_doc = self if (pr == self.name) else frappe.get_doc("Purchase Receipt", pr)
|
||||||
pr_doc.update_billing_percentage(update_modified=update_modified)
|
update_billing_percentage(pr_doc, update_modified=update_modified)
|
||||||
|
|
||||||
self.load_from_db()
|
self.load_from_db()
|
||||||
|
|
||||||
@ -500,7 +513,7 @@ def update_billed_amount_based_on_po(po_detail, update_modified=True):
|
|||||||
where po_detail=%s and (pr_detail is null or pr_detail = '') and docstatus=1""", po_detail)
|
where po_detail=%s and (pr_detail is null or pr_detail = '') and docstatus=1""", po_detail)
|
||||||
billed_against_po = billed_against_po and billed_against_po[0][0] or 0
|
billed_against_po = billed_against_po and billed_against_po[0][0] or 0
|
||||||
|
|
||||||
# Get all Delivery Note Item rows against the Sales Order Item row
|
# Get all Purchase Receipt Item rows against the Purchase Order Item row
|
||||||
pr_details = frappe.db.sql("""select pr_item.name, pr_item.amount, pr_item.parent
|
pr_details = frappe.db.sql("""select pr_item.name, pr_item.amount, pr_item.parent
|
||||||
from `tabPurchase Receipt Item` pr_item, `tabPurchase Receipt` pr
|
from `tabPurchase Receipt Item` pr_item, `tabPurchase Receipt` pr
|
||||||
where pr.name=pr_item.parent and pr_item.purchase_order_item=%s
|
where pr.name=pr_item.parent and pr_item.purchase_order_item=%s
|
||||||
@ -530,6 +543,39 @@ def update_billed_amount_based_on_po(po_detail, update_modified=True):
|
|||||||
|
|
||||||
return updated_pr
|
return updated_pr
|
||||||
|
|
||||||
|
def update_billing_percentage(pr_doc, update_modified=True):
|
||||||
|
# Reload as billed amount was set in db directly
|
||||||
|
pr_doc.load_from_db()
|
||||||
|
|
||||||
|
# Update Billing % based on pending accepted qty
|
||||||
|
total_amount, total_billed_amount = 0, 0
|
||||||
|
for item in pr_doc.items:
|
||||||
|
return_data = frappe.db.get_list("Purchase Receipt",
|
||||||
|
fields = [
|
||||||
|
"sum(abs(`tabPurchase Receipt Item`.qty)) as qty"
|
||||||
|
],
|
||||||
|
filters = [
|
||||||
|
["Purchase Receipt", "docstatus", "=", 1],
|
||||||
|
["Purchase Receipt", "is_return", "=", 1],
|
||||||
|
["Purchase Receipt Item", "purchase_receipt_item", "=", item.name]
|
||||||
|
])
|
||||||
|
|
||||||
|
returned_qty = return_data[0].qty if return_data else 0
|
||||||
|
returned_amount = flt(returned_qty) * flt(item.rate)
|
||||||
|
pending_amount = flt(item.amount) - returned_amount
|
||||||
|
total_billable_amount = pending_amount if item.billed_amt <= pending_amount else item.billed_amt
|
||||||
|
|
||||||
|
total_amount += total_billable_amount
|
||||||
|
total_billed_amount += flt(item.billed_amt)
|
||||||
|
|
||||||
|
percent_billed = round(100 * (total_billed_amount / (total_amount or 1)), 6)
|
||||||
|
pr_doc.db_set("per_billed", percent_billed)
|
||||||
|
pr_doc.load_from_db()
|
||||||
|
|
||||||
|
if update_modified:
|
||||||
|
pr_doc.set_status(update=True)
|
||||||
|
pr_doc.notify_update()
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_purchase_invoice(source_name, target_doc=None):
|
def make_purchase_invoice(source_name, target_doc=None):
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
@ -552,6 +598,7 @@ def make_purchase_invoice(source_name, target_doc=None):
|
|||||||
|
|
||||||
def update_item(source_doc, target_doc, source_parent):
|
def update_item(source_doc, target_doc, source_parent):
|
||||||
target_doc.qty, returned_qty = get_pending_qty(source_doc)
|
target_doc.qty, returned_qty = get_pending_qty(source_doc)
|
||||||
|
target_doc.stock_qty = flt(target_doc.qty) * flt(target_doc.conversion_factor, target_doc.precision("conversion_factor"))
|
||||||
returned_qty_map[source_doc.name] = returned_qty
|
returned_qty_map[source_doc.name] = returned_qty
|
||||||
|
|
||||||
def get_pending_qty(item_row):
|
def get_pending_qty(item_row):
|
||||||
|
@ -6,9 +6,11 @@ frappe.listview_settings['Purchase Receipt'] = {
|
|||||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||||
} else if (doc.status === "Closed") {
|
} else if (doc.status === "Closed") {
|
||||||
return [__("Closed"), "green", "status,=,Closed"];
|
return [__("Closed"), "green", "status,=,Closed"];
|
||||||
|
} else if (flt(doc.per_returned, 2) === 100) {
|
||||||
|
return [__("Return Issued"), "grey", "per_returned,=,100"];
|
||||||
} else if (flt(doc.grand_total) !== 0 && flt(doc.per_billed, 2) < 100) {
|
} else if (flt(doc.grand_total) !== 0 && flt(doc.per_billed, 2) < 100) {
|
||||||
return [__("To Bill"), "orange", "per_billed,<,100"];
|
return [__("To Bill"), "orange", "per_billed,<,100"];
|
||||||
} else if (flt(doc.grand_total) === 0 || flt(doc.per_billed, 2) == 100) {
|
} else if (flt(doc.grand_total) === 0 || flt(doc.per_billed, 2) === 100) {
|
||||||
return [__("Completed"), "green", "per_billed,=,100"];
|
return [__("Completed"), "green", "per_billed,=,100"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,10 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
self.assertFalse(frappe.db.get_all('Serial No', {'batch_no': batch_no}))
|
self.assertFalse(frappe.db.get_all('Serial No', {'batch_no': batch_no}))
|
||||||
|
|
||||||
def test_purchase_receipt_gl_entry(self):
|
def test_purchase_receipt_gl_entry(self):
|
||||||
pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", get_multiple_items = True, get_taxes_and_charges = True)
|
pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
|
||||||
|
warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1",
|
||||||
|
get_multiple_items = True, get_taxes_and_charges = True)
|
||||||
|
|
||||||
self.assertEqual(cint(erpnext.is_perpetual_inventory_enabled(pr.company)), 1)
|
self.assertEqual(cint(erpnext.is_perpetual_inventory_enabled(pr.company)), 1)
|
||||||
|
|
||||||
gl_entries = get_gl_entries("Purchase Receipt", pr.name)
|
gl_entries = get_gl_entries("Purchase Receipt", pr.name)
|
||||||
@ -281,11 +284,15 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
self.assertEqual(frappe.db.get_value("Serial No", serial_no, "warehouse"),
|
self.assertEqual(frappe.db.get_value("Serial No", serial_no, "warehouse"),
|
||||||
pr.get("items")[0].rejected_warehouse)
|
pr.get("items")[0].rejected_warehouse)
|
||||||
|
|
||||||
def test_purchase_return(self):
|
def test_purchase_return_partial(self):
|
||||||
|
pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
|
||||||
|
warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1")
|
||||||
|
|
||||||
pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1")
|
return_pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
|
||||||
|
warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1",
|
||||||
return_pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", is_return=1, return_against=pr.name, qty=-2)
|
is_return=1, return_against=pr.name, qty=-2, do_not_submit=1)
|
||||||
|
return_pr.items[0].purchase_receipt_item = pr.items[0].name
|
||||||
|
return_pr.submit()
|
||||||
|
|
||||||
# check sle
|
# check sle
|
||||||
outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
|
outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
|
||||||
@ -309,6 +316,60 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
self.assertEqual(expected_values[gle.account][0], gle.debit)
|
self.assertEqual(expected_values[gle.account][0], gle.debit)
|
||||||
self.assertEqual(expected_values[gle.account][1], gle.credit)
|
self.assertEqual(expected_values[gle.account][1], gle.credit)
|
||||||
|
|
||||||
|
# hack because new_doc isn't considering is_return portion of status_updater
|
||||||
|
returned = frappe.get_doc("Purchase Receipt", return_pr.name)
|
||||||
|
returned.update_prevdoc_status()
|
||||||
|
pr.load_from_db()
|
||||||
|
|
||||||
|
# Check if Original PR updated
|
||||||
|
self.assertEqual(pr.items[0].returned_qty, 2)
|
||||||
|
self.assertEqual(pr.per_returned, 40)
|
||||||
|
|
||||||
|
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||||
|
return_pr_2 = make_return_doc("Purchase Receipt", pr.name)
|
||||||
|
|
||||||
|
# Check if unreturned amount is mapped in 2nd return
|
||||||
|
self.assertEqual(return_pr_2.items[0].qty, -3)
|
||||||
|
|
||||||
|
# Make PI against unreturned amount
|
||||||
|
pi = make_purchase_invoice(pr.name)
|
||||||
|
pi.submit()
|
||||||
|
|
||||||
|
self.assertEqual(pi.items[0].qty, 3)
|
||||||
|
|
||||||
|
pr.load_from_db()
|
||||||
|
# PR should be completed on billing all unreturned amount
|
||||||
|
self.assertEqual(pr.items[0].billed_amt, 150)
|
||||||
|
self.assertEqual(pr.per_billed, 100)
|
||||||
|
self.assertEqual(pr.status, 'Completed')
|
||||||
|
|
||||||
|
pi.load_from_db()
|
||||||
|
pi.cancel()
|
||||||
|
|
||||||
|
pr.load_from_db()
|
||||||
|
self.assertEqual(pr.per_billed, 0)
|
||||||
|
|
||||||
|
return_pr.cancel()
|
||||||
|
pr.cancel()
|
||||||
|
|
||||||
|
def test_purchase_return_full(self):
|
||||||
|
pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
|
||||||
|
supplier_warehouse = "Work in Progress - TCP1")
|
||||||
|
|
||||||
|
return_pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
|
||||||
|
supplier_warehouse = "Work in Progress - TCP1", is_return=1, return_against=pr.name, qty=-5, do_not_submit=1)
|
||||||
|
return_pr.items[0].purchase_receipt_item = pr.items[0].name
|
||||||
|
return_pr.submit()
|
||||||
|
|
||||||
|
# hack because new_doc isn't considering is_return portion of status_updater
|
||||||
|
returned = frappe.get_doc("Purchase Receipt", return_pr.name)
|
||||||
|
returned.update_prevdoc_status()
|
||||||
|
pr.load_from_db()
|
||||||
|
|
||||||
|
# Check if Original PR updated
|
||||||
|
self.assertEqual(pr.items[0].returned_qty, 5)
|
||||||
|
self.assertEqual(pr.per_returned, 100)
|
||||||
|
self.assertEqual(pr.status, 'Return Issued')
|
||||||
|
|
||||||
def test_purchase_return_for_rejected_qty(self):
|
def test_purchase_return_for_rejected_qty(self):
|
||||||
from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
|
from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
|
||||||
@ -416,6 +477,7 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
self.assertEqual(pr1.per_billed, 100)
|
self.assertEqual(pr1.per_billed, 100)
|
||||||
self.assertEqual(pr1.status, "Completed")
|
self.assertEqual(pr1.status, "Completed")
|
||||||
|
|
||||||
|
pr2.load_from_db()
|
||||||
self.assertEqual(pr2.get("items")[0].billed_amt, 2000)
|
self.assertEqual(pr2.get("items")[0].billed_amt, 2000)
|
||||||
self.assertEqual(pr2.per_billed, 80)
|
self.assertEqual(pr2.per_billed, 80)
|
||||||
self.assertEqual(pr2.status, "To Bill")
|
self.assertEqual(pr2.status, "To Bill")
|
||||||
|
@ -28,9 +28,13 @@
|
|||||||
"uom",
|
"uom",
|
||||||
"stock_uom",
|
"stock_uom",
|
||||||
"conversion_factor",
|
"conversion_factor",
|
||||||
"stock_qty",
|
|
||||||
"retain_sample",
|
"retain_sample",
|
||||||
"sample_quantity",
|
"sample_quantity",
|
||||||
|
"tracking_section",
|
||||||
|
"received_stock_qty",
|
||||||
|
"stock_qty",
|
||||||
|
"col_break_tracking_section",
|
||||||
|
"returned_qty",
|
||||||
"rate_and_amount",
|
"rate_and_amount",
|
||||||
"price_list_rate",
|
"price_list_rate",
|
||||||
"discount_percentage",
|
"discount_percentage",
|
||||||
@ -526,7 +530,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "stock_qty",
|
"fieldname": "stock_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Accepted Qty as per Stock UOM",
|
"label": "Accepted Qty in Stock UOM",
|
||||||
"oldfieldname": "stock_qty",
|
"oldfieldname": "stock_qty",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
@ -834,12 +838,35 @@
|
|||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
"fieldname": "image_column",
|
"fieldname": "image_column",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "tracking_section",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "col_break_tracking_section",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "returned_qty",
|
||||||
|
"fieldname": "returned_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Returned Qty in Stock UOM",
|
||||||
|
"no_copy": 1,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "received_stock_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Received Qty in Stock UOM",
|
||||||
|
"print_hide": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-04-28 19:01:21.154963",
|
"modified": "2020-11-02 10:00:38.204294",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Purchase Receipt Item",
|
"name": "Purchase Receipt Item",
|
||||||
|
@ -55,7 +55,7 @@ class StockSettings(Document):
|
|||||||
""")
|
""")
|
||||||
|
|
||||||
if sle:
|
if sle:
|
||||||
frappe.throw(_("Can't change valuation method, as there are transactions against some items which does not have it's own valuation method"))
|
frappe.throw(_("Can't change the valuation method, as there are transactions against some items which do not have its own valuation method"))
|
||||||
|
|
||||||
def validate_clean_description_html(self):
|
def validate_clean_description_html(self):
|
||||||
if int(self.clean_description_html or 0) \
|
if int(self.clean_description_html or 0) \
|
||||||
|
@ -164,7 +164,7 @@ def get_stock_ledger_entries(filters, items):
|
|||||||
select
|
select
|
||||||
sle.item_code, warehouse, sle.posting_date, sle.actual_qty, sle.valuation_rate,
|
sle.item_code, warehouse, sle.posting_date, sle.actual_qty, sle.valuation_rate,
|
||||||
sle.company, sle.voucher_type, sle.qty_after_transaction, sle.stock_value_difference,
|
sle.company, sle.voucher_type, sle.qty_after_transaction, sle.stock_value_difference,
|
||||||
sle.item_code as name, sle.voucher_no
|
sle.item_code as name, sle.voucher_no, sle.stock_value
|
||||||
from
|
from
|
||||||
`tabStock Ledger Entry` sle force index (posting_sort_index)
|
`tabStock Ledger Entry` sle force index (posting_sort_index)
|
||||||
where sle.docstatus < 2 %s %s
|
where sle.docstatus < 2 %s %s
|
||||||
@ -197,7 +197,7 @@ def get_item_warehouse_map(filters, sle):
|
|||||||
else:
|
else:
|
||||||
qty_diff = flt(d.actual_qty)
|
qty_diff = flt(d.actual_qty)
|
||||||
|
|
||||||
value_diff = flt(d.stock_value_difference)
|
value_diff = flt(d.stock_value) - flt(qty_dict.bal_val)
|
||||||
|
|
||||||
if d.posting_date < from_date:
|
if d.posting_date < from_date:
|
||||||
qty_dict.opening_qty += qty_diff
|
qty_dict.opening_qty += qty_diff
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
frappe.ui.form.on("Issue", {
|
frappe.ui.form.on("Issue", {
|
||||||
onload: function(frm) {
|
onload: function(frm) {
|
||||||
frm.email_field = "raised_by";
|
frm.email_field = "raised_by";
|
||||||
|
frm.set_query("customer", function () {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
"disabled": 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
frappe.db.get_value("Support Settings", {name: "Support Settings"},
|
frappe.db.get_value("Support Settings", {name: "Support Settings"},
|
||||||
["allow_resetting_service_level_agreement", "track_service_level_agreement"], (r) => {
|
["allow_resetting_service_level_agreement", "track_service_level_agreement"], (r) => {
|
||||||
@ -21,14 +28,14 @@ frappe.ui.form.on("Issue", {
|
|||||||
},
|
},
|
||||||
callback: function (r) {
|
callback: function (r) {
|
||||||
if (r && r.message) {
|
if (r && r.message) {
|
||||||
frm.set_query('priority', function() {
|
frm.set_query("priority", function() {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"name": ["in", r.message.priority],
|
"name": ["in", r.message.priority],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
frm.set_query('service_level_agreement', function() {
|
frm.set_query("service_level_agreement", function() {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"name": ["in", r.message.service_level_agreements],
|
"name": ["in", r.message.service_level_agreements],
|
||||||
@ -45,9 +52,9 @@ frappe.ui.form.on("Issue", {
|
|||||||
if (frm.doc.status !== "Closed" && frm.doc.agreement_status === "Ongoing") {
|
if (frm.doc.status !== "Closed" && frm.doc.agreement_status === "Ongoing") {
|
||||||
if (frm.doc.service_level_agreement) {
|
if (frm.doc.service_level_agreement) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
'method': 'frappe.client.get',
|
"method": "frappe.client.get",
|
||||||
args: {
|
args: {
|
||||||
doctype: 'Service Level Agreement',
|
doctype: "Service Level Agreement",
|
||||||
name: frm.doc.service_level_agreement
|
name: frm.doc.service_level_agreement
|
||||||
},
|
},
|
||||||
callback: function(data) {
|
callback: function(data) {
|
||||||
@ -127,8 +134,8 @@ frappe.ui.form.on("Issue", {
|
|||||||
reset_sla.clear();
|
reset_sla.clear();
|
||||||
|
|
||||||
frappe.show_alert({
|
frappe.show_alert({
|
||||||
indicator: 'green',
|
indicator: "green",
|
||||||
message: __('Resetting Service Level Agreement.')
|
message: __("Resetting Service Level Agreement.")
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.call("reset_service_level_agreement", {
|
frm.call("reset_service_level_agreement", {
|
||||||
@ -145,35 +152,36 @@ frappe.ui.form.on("Issue", {
|
|||||||
reset_sla.show();
|
reset_sla.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
timeline_refresh: function(frm) {
|
timeline_refresh: function(frm) {
|
||||||
// create button for "Help Article"
|
// create button for "Help Article"
|
||||||
if(frappe.model.can_create('Help Article')) {
|
if (frappe.model.can_create("Help Article")) {
|
||||||
// Removing Help Article button if exists to avoid multiple occurance
|
// Removing Help Article button if exists to avoid multiple occurance
|
||||||
frm.timeline.wrapper.find('.comment-header .asset-details .btn-add-to-kb').remove();
|
frm.timeline.wrapper.find('.comment-header .asset-details .btn-add-to-kb').remove();
|
||||||
$('<button class="btn btn-xs btn-link btn-add-to-kb text-muted hidden-xs pull-right">'+
|
$('<button class="btn btn-xs btn-link btn-add-to-kb text-muted hidden-xs pull-right">'+
|
||||||
__('Help Article') + '</button>')
|
__('Help Article') + '</button>')
|
||||||
.appendTo(frm.timeline.wrapper.find('.comment-header .asset-details:not([data-communication-type="Comment"])'))
|
.appendTo(frm.timeline.wrapper.find('.comment-header .asset-details:not([data-communication-type="Comment"])'))
|
||||||
.on('click', function() {
|
.on("click", function() {
|
||||||
var content = $(this).parents('.timeline-item:first').find('.timeline-item-content').html();
|
var content = $(this).parents(".timeline-item:first").find(".timeline-item-content").html();
|
||||||
var doc = frappe.model.get_new_doc('Help Article');
|
var doc = frappe.model.get_new_doc("Help Article");
|
||||||
doc.title = frm.doc.subject;
|
doc.title = frm.doc.subject;
|
||||||
doc.content = content;
|
doc.content = content;
|
||||||
frappe.set_route('Form', 'Help Article', doc.name);
|
frappe.set_route("Form", "Help Article", doc.name);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!frm.timeline.wrapper.find('.btn-split-issue').length) {
|
if (!frm.timeline.wrapper.find(".btn-split-issue").length) {
|
||||||
let split_issue = __("Split Issue")
|
let split_issue = __("Split Issue")
|
||||||
$(`<button class="btn btn-xs btn-link btn-add-to-kb text-muted hidden-xs btn-split-issue pull-right" style="display:inline-block; margin-right: 15px">
|
$(`<button class="btn btn-xs btn-link btn-add-to-kb text-muted hidden-xs btn-split-issue pull-right" style="display:inline-block; margin-right: 15px">
|
||||||
${split_issue}
|
${split_issue}
|
||||||
</button>`)
|
</button>`)
|
||||||
.appendTo(frm.timeline.wrapper.find('.comment-header .asset-details:not([data-communication-type="Comment"])'))
|
.appendTo(frm.timeline.wrapper.find('.comment-header .asset-details:not([data-communication-type="Comment"])'))
|
||||||
if (!frm.timeline.wrapper.data("split-issue-event-attached")){
|
if (!frm.timeline.wrapper.data("split-issue-event-attached")){
|
||||||
frm.timeline.wrapper.on('click', '.btn-split-issue', (e) => {
|
frm.timeline.wrapper.on("click", ".btn-split-issue", (e) => {
|
||||||
var dialog = new frappe.ui.Dialog({
|
var dialog = new frappe.ui.Dialog({
|
||||||
title: __("Split Issue"),
|
title: __("Split Issue"),
|
||||||
fields: [
|
fields: [
|
||||||
{fieldname: 'subject', fieldtype: 'Data', reqd:1, label: __('Subject'), description: __('All communications including and above this shall be moved into the new Issue')}
|
{fieldname: "subject", fieldtype: "Data", reqd: 1, label: __("Subject"), description: __("All communications including and above this shall be moved into the new Issue")}
|
||||||
],
|
],
|
||||||
primary_action_label: __("Split"),
|
primary_action_label: __("Split"),
|
||||||
primary_action: function() {
|
primary_action: function() {
|
||||||
@ -226,7 +234,7 @@ function set_time_to_resolve_and_response(frm) {
|
|||||||
function get_time_left(timestamp, agreement_status) {
|
function get_time_left(timestamp, agreement_status) {
|
||||||
const diff = moment(timestamp).diff(moment());
|
const diff = moment(timestamp).diff(moment());
|
||||||
const diff_display = diff >= 44500 ? moment.duration(diff).humanize() : "Failed";
|
const diff_display = diff >= 44500 ? moment.duration(diff).humanize() : "Failed";
|
||||||
let indicator = (diff_display == 'Failed' && agreement_status != "Fulfilled") ? "red" : "green";
|
let indicator = (diff_display == "Failed" && agreement_status != "Fulfilled") ? "red" : "green";
|
||||||
return {"diff_display": diff_display, "indicator": indicator};
|
return {"diff_display": diff_display, "indicator": indicator};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user