Merge branch 'develop' into fix_picked_qty_in_DN
This commit is contained in:
commit
7f9fedf08a
@ -43,7 +43,7 @@
|
||||
}
|
||||
],
|
||||
"grand_total": 0,
|
||||
"naming_series": "_T-BILL",
|
||||
"naming_series": "T-PINV-",
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": "_Test Account Shipping Charges - _TC",
|
||||
@ -167,7 +167,7 @@
|
||||
}
|
||||
],
|
||||
"grand_total": 0,
|
||||
"naming_series": "_T-Purchase Invoice-",
|
||||
"naming_series": "T-PINV-",
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": "_Test Account Shipping Charges - _TC",
|
||||
|
@ -31,7 +31,7 @@
|
||||
"base_grand_total": 561.8,
|
||||
"grand_total": 561.8,
|
||||
"is_pos": 0,
|
||||
"naming_series": "_T-Sales Invoice-",
|
||||
"naming_series": "T-SINV-",
|
||||
"base_net_total": 500.0,
|
||||
"taxes": [
|
||||
{
|
||||
@ -104,7 +104,7 @@
|
||||
"base_grand_total": 630.0,
|
||||
"grand_total": 630.0,
|
||||
"is_pos": 0,
|
||||
"naming_series": "_T-Sales Invoice-",
|
||||
"naming_series": "T-SINV-",
|
||||
"base_net_total": 500.0,
|
||||
"taxes": [
|
||||
{
|
||||
@ -175,7 +175,7 @@
|
||||
],
|
||||
"grand_total": 0,
|
||||
"is_pos": 0,
|
||||
"naming_series": "_T-Sales Invoice-",
|
||||
"naming_series": "T-SINV-",
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": "_Test Account Shipping Charges - _TC",
|
||||
@ -301,7 +301,7 @@
|
||||
],
|
||||
"grand_total": 0,
|
||||
"is_pos": 0,
|
||||
"naming_series": "_T-Sales Invoice-",
|
||||
"naming_series": "T-SINV-",
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": "_Test Account Excise Duty - _TC",
|
||||
|
@ -2115,6 +2115,7 @@ def create_sales_invoice(**args):
|
||||
si.return_against = args.return_against
|
||||
si.currency=args.currency or "INR"
|
||||
si.conversion_rate = args.conversion_rate or 1
|
||||
si.naming_series = args.naming_series or "T-SINV-"
|
||||
|
||||
si.append("items", {
|
||||
"item_code": args.item or args.item_code or "_Test Item",
|
||||
|
@ -253,6 +253,7 @@ class PurchaseOrder(BuyingController):
|
||||
self.update_prevdoc_status()
|
||||
|
||||
# Must be called after updating ordered qty in Material Request
|
||||
# bin uses Material Request Items to recalculate & update
|
||||
self.update_requested_qty()
|
||||
self.update_ordered_qty()
|
||||
|
||||
|
@ -90,6 +90,50 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
frappe.db.set_value('Item', '_Test Item', 'over_billing_allowance', 0)
|
||||
frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0)
|
||||
|
||||
def test_update_remove_child_linked_to_mr(self):
|
||||
"""Test impact on linked PO and MR on deleting/updating row."""
|
||||
mr = make_material_request(qty=10)
|
||||
po = make_purchase_order(mr.name)
|
||||
po.supplier = "_Test Supplier"
|
||||
po.save()
|
||||
po.submit()
|
||||
|
||||
first_item_of_po = po.get("items")[0]
|
||||
existing_ordered_qty = get_ordered_qty() # 10
|
||||
existing_requested_qty = get_requested_qty() # 0
|
||||
|
||||
# decrease ordered qty by 3 (10 -> 7) and add item
|
||||
trans_item = json.dumps([
|
||||
{
|
||||
'item_code': first_item_of_po.item_code,
|
||||
'rate': first_item_of_po.rate,
|
||||
'qty': 7,
|
||||
'docname': first_item_of_po.name
|
||||
},
|
||||
{'item_code' : '_Test Item 2', 'rate' : 200, 'qty' : 2}
|
||||
])
|
||||
update_child_qty_rate('Purchase Order', trans_item, po.name)
|
||||
mr.reload()
|
||||
|
||||
# requested qty increases as ordered qty decreases
|
||||
self.assertEqual(get_requested_qty(), existing_requested_qty + 3) # 3
|
||||
self.assertEqual(mr.items[0].ordered_qty, 7)
|
||||
|
||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty - 3) # 7
|
||||
|
||||
# delete first item linked to Material Request
|
||||
trans_item = json.dumps([
|
||||
{'item_code' : '_Test Item 2', 'rate' : 200, 'qty' : 2}
|
||||
])
|
||||
update_child_qty_rate('Purchase Order', trans_item, po.name)
|
||||
mr.reload()
|
||||
|
||||
# requested qty increases as ordered qty is 0 (deleted row)
|
||||
self.assertEqual(get_requested_qty(), existing_requested_qty + 10) # 10
|
||||
self.assertEqual(mr.items[0].ordered_qty, 0)
|
||||
|
||||
# ordered qty decreases as ordered qty is 0 (deleted row)
|
||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty - 10) # 0
|
||||
|
||||
def test_update_child(self):
|
||||
mr = make_material_request(qty=10)
|
||||
@ -120,7 +164,6 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEqual(po.get("items")[0].amount, 1400)
|
||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
|
||||
|
||||
|
||||
def test_update_child_adding_new_item(self):
|
||||
po = create_purchase_order(do_not_save=1)
|
||||
po.items[0].qty = 4
|
||||
@ -129,6 +172,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
pr = make_pr_against_po(po.name, 2)
|
||||
|
||||
po.load_from_db()
|
||||
existing_ordered_qty = get_ordered_qty()
|
||||
first_item_of_po = po.get("items")[0]
|
||||
|
||||
trans_item = json.dumps([
|
||||
@ -145,7 +189,8 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
po.reload()
|
||||
self.assertEquals(len(po.get('items')), 2)
|
||||
self.assertEqual(po.status, 'To Receive and Bill')
|
||||
|
||||
# ordered qty should increase on row addition
|
||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 7)
|
||||
|
||||
def test_update_child_removing_item(self):
|
||||
po = create_purchase_order(do_not_save=1)
|
||||
@ -156,6 +201,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
|
||||
po.reload()
|
||||
first_item_of_po = po.get("items")[0]
|
||||
existing_ordered_qty = get_ordered_qty()
|
||||
# add an item
|
||||
trans_item = json.dumps([
|
||||
{
|
||||
@ -168,6 +214,10 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
update_child_qty_rate('Purchase Order', trans_item, po.name)
|
||||
|
||||
po.reload()
|
||||
|
||||
# ordered qty should increase on row addition
|
||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 7)
|
||||
|
||||
# check if can remove received item
|
||||
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.get("items")[1].name}])
|
||||
self.assertRaises(frappe.ValidationError, update_child_qty_rate, 'Purchase Order', trans_item, po.name)
|
||||
@ -187,6 +237,9 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEquals(len(po.get('items')), 1)
|
||||
self.assertEqual(po.status, 'To Receive and Bill')
|
||||
|
||||
# ordered qty should decrease (back to initial) on row deletion
|
||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty)
|
||||
|
||||
def test_update_child_perm(self):
|
||||
po = create_purchase_order(item_code= "_Test Item", qty=4)
|
||||
|
||||
@ -230,11 +283,13 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
|
||||
new_item_with_tax = frappe.get_doc("Item", "Test Item with Tax")
|
||||
|
||||
new_item_with_tax.append("taxes", {
|
||||
"item_tax_template": "Test Update Items Template - _TC",
|
||||
"valid_from": nowdate()
|
||||
})
|
||||
new_item_with_tax.save()
|
||||
if not frappe.db.exists("Item Tax",
|
||||
{"item_tax_template": "Test Update Items Template - _TC", "parent": "Test Item with Tax"}):
|
||||
new_item_with_tax.append("taxes", {
|
||||
"item_tax_template": "Test Update Items Template - _TC",
|
||||
"valid_from": nowdate()
|
||||
})
|
||||
new_item_with_tax.save()
|
||||
|
||||
tax_template = "_Test Account Excise Duty @ 10 - _TC"
|
||||
item = "_Test Item Home Desktop 100"
|
||||
|
@ -9,9 +9,7 @@ import unittest
|
||||
class TestSupplierScorecard(unittest.TestCase):
|
||||
|
||||
def test_create_scorecard(self):
|
||||
delete_test_scorecards()
|
||||
my_doc = make_supplier_scorecard()
|
||||
doc = my_doc.insert()
|
||||
doc = make_supplier_scorecard().insert()
|
||||
self.assertEqual(doc.name, valid_scorecard[0].get("supplier"))
|
||||
|
||||
def test_criteria_weight(self):
|
||||
@ -121,7 +119,8 @@ valid_scorecard = [
|
||||
{
|
||||
"weight":100.0,
|
||||
"doctype":"Supplier Scorecard Scoring Criteria",
|
||||
"criteria_name":"Delivery"
|
||||
"criteria_name":"Delivery",
|
||||
"formula": "100"
|
||||
}
|
||||
],
|
||||
"supplier":"_Test Supplier",
|
||||
|
@ -1336,25 +1336,63 @@ def set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child
|
||||
p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
|
||||
child_item = frappe.new_doc(child_doctype, p_doc, child_docname)
|
||||
item = frappe.get_doc("Item", trans_item.get('item_code'))
|
||||
|
||||
for field in ("item_code", "item_name", "description", "item_group"):
|
||||
child_item.update({field: item.get(field)})
|
||||
child_item.update({field: item.get(field)})
|
||||
|
||||
date_fieldname = "delivery_date" if child_doctype == "Sales Order Item" else "schedule_date"
|
||||
child_item.update({date_fieldname: trans_item.get(date_fieldname) or p_doc.get(date_fieldname)})
|
||||
child_item.stock_uom = item.stock_uom
|
||||
child_item.uom = trans_item.get("uom") or item.stock_uom
|
||||
child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
|
||||
conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor"))
|
||||
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor
|
||||
|
||||
if child_doctype == "Purchase Order Item":
|
||||
child_item.base_rate = 1 # Initiallize value will update in parent validation
|
||||
child_item.base_amount = 1 # Initiallize value will update in parent validation
|
||||
# Initialized value will update in parent validation
|
||||
child_item.base_rate = 1
|
||||
child_item.base_amount = 1
|
||||
if child_doctype == "Sales Order Item":
|
||||
child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
|
||||
if not child_item.warehouse:
|
||||
frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
|
||||
.format(frappe.bold("default warehouse"), frappe.bold(item.item_code)))
|
||||
|
||||
set_child_tax_template_and_map(item, child_item, p_doc)
|
||||
add_taxes_from_tax_template(child_item, p_doc)
|
||||
return child_item
|
||||
|
||||
def validate_child_on_delete(row, parent):
|
||||
"""Check if partially transacted item (row) is being deleted."""
|
||||
if parent.doctype == "Sales Order":
|
||||
if flt(row.delivered_qty):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been delivered").format(row.idx, row.item_code))
|
||||
if flt(row.work_order_qty):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has work order assigned to it.").format(row.idx, row.item_code))
|
||||
if flt(row.ordered_qty):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.").format(row.idx, row.item_code))
|
||||
|
||||
if parent.doctype == "Purchase Order" and flt(row.received_qty):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been received").format(row.idx, row.item_code))
|
||||
|
||||
if flt(row.billed_amt):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been billed.").format(row.idx, row.item_code))
|
||||
|
||||
def update_bin_on_delete(row, doctype):
|
||||
"""Update bin for deleted item (row)."""
|
||||
from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty, get_ordered_qty, get_indented_qty
|
||||
qty_dict = {}
|
||||
|
||||
if doctype == "Sales Order":
|
||||
qty_dict["reserved_qty"] = get_reserved_qty(row.item_code, row.warehouse)
|
||||
else:
|
||||
if row.material_request_item:
|
||||
qty_dict["indented_qty"] = get_indented_qty(row.item_code, row.warehouse)
|
||||
|
||||
qty_dict["ordered_qty"] = get_ordered_qty(row.item_code, row.warehouse)
|
||||
|
||||
update_bin_qty(row.item_code, row.warehouse, qty_dict)
|
||||
|
||||
def validate_and_delete_children(parent, data):
|
||||
deleted_children = []
|
||||
updated_item_names = [d.get("docname") for d in data]
|
||||
@ -1363,23 +1401,17 @@ def validate_and_delete_children(parent, data):
|
||||
deleted_children.append(item)
|
||||
|
||||
for d in deleted_children:
|
||||
if parent.doctype == "Sales Order":
|
||||
if flt(d.delivered_qty):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been delivered").format(d.idx, d.item_code))
|
||||
if flt(d.work_order_qty):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has work order assigned to it.").format(d.idx, d.item_code))
|
||||
if flt(d.ordered_qty):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.").format(d.idx, d.item_code))
|
||||
|
||||
if parent.doctype == "Purchase Order" and flt(d.received_qty):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been received").format(d.idx, d.item_code))
|
||||
|
||||
if flt(d.billed_amt):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been billed.").format(d.idx, d.item_code))
|
||||
|
||||
validate_child_on_delete(d, parent)
|
||||
d.cancel()
|
||||
d.delete()
|
||||
|
||||
# need to update ordered qty in Material Request first
|
||||
# bin uses Material Request Items to recalculate & update
|
||||
parent.update_prevdoc_status()
|
||||
|
||||
for d in deleted_children:
|
||||
update_bin_on_delete(d, parent.doctype)
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
|
||||
def check_doc_permissions(doc, perm_type='create'):
|
||||
|
@ -181,7 +181,6 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "Company:company:default_currency",
|
||||
"depends_on": "eval:(doc.docstatus==1 || doc.employee)",
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
@ -201,7 +200,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-11-25 12:01:55.980721",
|
||||
"modified": "2021-03-31 14:42:47.321368",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Employee Advance",
|
||||
|
@ -130,7 +130,6 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "Company:company:default_currency",
|
||||
"depends_on": "eval:(doc.docstatus==1 || doc.employee)",
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
@ -155,7 +154,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-11-25 11:56:06.777241",
|
||||
"modified": "2021-03-31 14:45:27.948207",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Leave Encashment",
|
||||
|
@ -25,6 +25,16 @@ frappe.ui.form.on('Production Plan', {
|
||||
}
|
||||
});
|
||||
|
||||
frm.set_query('material_request', 'material_requests', function() {
|
||||
return {
|
||||
filters: {
|
||||
material_request_type: "Manufacture",
|
||||
docstatus: 1,
|
||||
status: ["!=", "Stopped"],
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.fields_dict['po_items'].grid.get_field('item_code').get_query = function(doc) {
|
||||
return {
|
||||
query: "erpnext.controllers.queries.item_query",
|
||||
@ -370,4 +380,4 @@ cur_frm.fields_dict['sales_orders'].grid.get_field("sales_order").get_query = fu
|
||||
['Sales Order','docstatus', '=' ,1]
|
||||
]
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -70,7 +70,7 @@ class ProductionPlan(Document):
|
||||
from `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
|
||||
where mr_item.parent = mr.name
|
||||
and mr.material_request_type = "Manufacture"
|
||||
and mr.docstatus = 1 and mr.company = %(company)s
|
||||
and mr.docstatus = 1 and mr.status != "Stopped" and mr.company = %(company)s
|
||||
and mr_item.qty > ifnull(mr_item.ordered_qty,0) {0} {1}
|
||||
and (exists (select name from `tabBOM` bom where bom.item=mr_item.item_code
|
||||
and bom.is_active = 1))
|
||||
|
@ -99,7 +99,7 @@ execute:frappe.delete_doc("DocType", "Purchase Request")
|
||||
execute:frappe.delete_doc("DocType", "Purchase Request Item")
|
||||
erpnext.patches.v4_2.recalculate_bom_cost
|
||||
erpnext.patches.v4_2.fix_gl_entries_for_stock_transactions
|
||||
erpnext.patches.v4_2.update_requested_and_ordered_qty
|
||||
erpnext.patches.v4_2.update_requested_and_ordered_qty #2021-03-31
|
||||
execute:frappe.rename_doc("DocType", "Support Ticket", "Issue", force=True)
|
||||
erpnext.patches.v4_4.make_email_accounts
|
||||
execute:frappe.delete_doc("DocType", "Contact Control")
|
||||
@ -208,7 +208,7 @@ erpnext.patches.v5_7.update_item_description_based_on_item_master
|
||||
erpnext.patches.v5_7.item_template_attributes
|
||||
execute:frappe.delete_doc_if_exists("DocType", "Manage Variants")
|
||||
execute:frappe.delete_doc_if_exists("DocType", "Manage Variants Item")
|
||||
erpnext.patches.v4_2.repost_reserved_qty #2016-04-15
|
||||
erpnext.patches.v4_2.repost_reserved_qty #2021-03-31
|
||||
erpnext.patches.v5_4.update_purchase_cost_against_project
|
||||
erpnext.patches.v5_8.update_order_reference_in_return_entries
|
||||
erpnext.patches.v5_8.add_credit_note_print_heading
|
||||
|
@ -163,7 +163,6 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "Company:company:default_currency",
|
||||
"depends_on": "eval:(doc.docstatus==1 || doc.employee)",
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
@ -176,7 +175,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-20 17:51:13.419716",
|
||||
"modified": "2021-03-31 14:45:48.566756",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Additional Salary",
|
||||
|
@ -124,7 +124,6 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "Company:company:default_currency",
|
||||
"depends_on": "eval:(doc.docstatus==1 || doc.employee)",
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
@ -148,7 +147,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-12-14 15:52:08.566418",
|
||||
"modified": "2021-03-31 14:46:22.465521",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Employee Benefit Application",
|
||||
|
@ -21,7 +21,6 @@ frappe.ui.form.on('Employee Benefit Claim', {
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
frm.set_value('currency', r.message);
|
||||
frm.set_df_property('currency', 'hidden', 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -125,10 +125,9 @@
|
||||
"label": "Attachments"
|
||||
},
|
||||
{
|
||||
"default": "Company:company:default_currency",
|
||||
"depends_on": "eval: doc.employee",
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"label": "Currency",
|
||||
"options": "Currency",
|
||||
"read_only": 1,
|
||||
@ -145,7 +144,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-11-25 11:49:56.097352",
|
||||
"modified": "2021-03-31 15:51:51.489269",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Employee Benefit Claim",
|
||||
|
@ -75,7 +75,6 @@
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "Company:company:default_currency",
|
||||
"depends_on": "eval:(doc.docstatus==1 || doc.employee)",
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
@ -95,7 +94,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-20 17:22:16.468042",
|
||||
"modified": "2021-03-31 14:48:00.919839",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Employee Incentive",
|
||||
|
@ -47,5 +47,26 @@ frappe.ui.form.on('Employee Tax Exemption Declaration', {
|
||||
});
|
||||
}).addClass("btn-primary");
|
||||
}
|
||||
},
|
||||
|
||||
employee: function(frm) {
|
||||
if (frm.doc.employee) {
|
||||
frm.trigger('get_employee_currency');
|
||||
}
|
||||
},
|
||||
|
||||
get_employee_currency: function(frm) {
|
||||
frappe.call({
|
||||
method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency",
|
||||
args: {
|
||||
employee: frm.doc.employee,
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
frm.set_value('currency', r.message);
|
||||
frm.refresh_fields();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -108,7 +108,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "Company:company:default_currency",
|
||||
"depends_on": "eval: doc.employee",
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
"label": "Currency",
|
||||
@ -119,7 +119,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-20 16:42:24.493761",
|
||||
"modified": "2021-03-31 20:41:57.387749",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Employee Tax Exemption Declaration",
|
||||
|
@ -58,5 +58,26 @@ frappe.ui.form.on('Employee Tax Exemption Proof Submission', {
|
||||
|
||||
currency: function(frm) {
|
||||
frm.refresh_fields();
|
||||
}
|
||||
},
|
||||
|
||||
employee: function(frm) {
|
||||
if (frm.doc.employee) {
|
||||
frm.trigger('get_employee_currency');
|
||||
}
|
||||
},
|
||||
|
||||
get_employee_currency: function(frm) {
|
||||
frappe.call({
|
||||
method: "erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment.get_employee_currency",
|
||||
args: {
|
||||
employee: frm.doc.employee,
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
frm.set_value('currency', r.message);
|
||||
frm.refresh_fields();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
@ -131,7 +131,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "Company:company:default_currency",
|
||||
"depends_on": "eval: doc.employee",
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
"label": "Currency",
|
||||
@ -142,7 +142,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-20 16:47:03.410020",
|
||||
"modified": "2021-03-31 20:48:32.639885",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Employee Tax Exemption Proof Submission",
|
||||
|
@ -93,7 +93,7 @@
|
||||
"options": "Income Tax Slab Other Charges"
|
||||
},
|
||||
{
|
||||
"default": "Company:company:default_currency",
|
||||
"fetch_from": "company.default_currency",
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
"label": "Currency",
|
||||
@ -104,7 +104,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-19 13:54:24.728075",
|
||||
"modified": "2021-03-31 20:53:33.323712",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Income Tax Slab",
|
||||
|
@ -93,7 +93,6 @@
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "Company:company:default_currency",
|
||||
"depends_on": "eval:(doc.docstatus==1 || doc.employee)",
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
@ -106,7 +105,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-20 17:27:47.003134",
|
||||
"modified": "2021-03-31 14:50:29.401020",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Retention Bonus",
|
||||
|
@ -216,7 +216,7 @@ frappe.ui.form.on('Salary Slip Timesheet', {
|
||||
});
|
||||
|
||||
var set_totals = function(frm) {
|
||||
if (frm.doc.docstatus === 0) {
|
||||
if (frm.doc.docstatus === 0 && frm.doc.doctype === "Salary Slip") {
|
||||
if (frm.doc.earnings || frm.doc.deductions) {
|
||||
frappe.call({
|
||||
method: "set_totals",
|
||||
|
@ -500,7 +500,6 @@
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "Company:company:default_currency",
|
||||
"depends_on": "eval:(doc.docstatus==1 || doc.salary_structure)",
|
||||
"fetch_from": "salary_structure.currency",
|
||||
"fieldname": "currency",
|
||||
@ -632,7 +631,7 @@
|
||||
"idx": 9,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-02-19 11:48:05.383945",
|
||||
"modified": "2021-03-31 15:39:28.817166",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Salary Slip",
|
||||
|
@ -232,7 +232,7 @@
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-30 11:30:32.190798",
|
||||
"modified": "2021-03-31 15:41:12.342380",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Salary Structure",
|
||||
|
@ -125,7 +125,6 @@
|
||||
"options": "Income Tax Slab"
|
||||
},
|
||||
{
|
||||
"default": "Company:company:default_currency",
|
||||
"depends_on": "eval:(doc.docstatus==1 || doc.salary_structure)",
|
||||
"fetch_from": "salary_structure.currency",
|
||||
"fieldname": "currency",
|
||||
@ -146,7 +145,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-11-30 18:07:48.251311",
|
||||
"modified": "2021-03-31 15:49:36.361253",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Salary Structure Assignment",
|
||||
|
@ -18,8 +18,8 @@ frappe.ui.form.on("Project", {
|
||||
};
|
||||
},
|
||||
onload: function (frm) {
|
||||
var so = frappe.meta.get_docfield("Project", "sales_order");
|
||||
so.get_route_options_for_new_doc = function (field) {
|
||||
const so = frm.get_docfield("sales_order");
|
||||
so.get_route_options_for_new_doc = () => {
|
||||
if (frm.is_new()) return;
|
||||
return {
|
||||
"customer": frm.doc.customer,
|
||||
|
@ -1167,6 +1167,11 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
this.calculate_net_weight();
|
||||
}
|
||||
|
||||
// for handling customization not to fetch price list rate
|
||||
if(frappe.flags.dont_fetch_price_list_rate) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!dont_fetch_price_list_rate &&
|
||||
frappe.meta.has_field(doc.doctype, "price_list_currency")) {
|
||||
this.apply_price_list(item, true);
|
||||
|
@ -919,7 +919,8 @@
|
||||
"minLength": 1,
|
||||
"maxLength": 15,
|
||||
"pattern": "^([0-9A-Z/-]){1,15}$",
|
||||
"description": "Tranport Document Number"
|
||||
"description": "Tranport Document Number",
|
||||
"validationMsg": "Transport Receipt No is invalid"
|
||||
},
|
||||
"TransDocDt": {
|
||||
"type": "string",
|
||||
|
@ -334,8 +334,11 @@ def make_einvoice(invoice):
|
||||
buyer_details = get_overseas_address_details(invoice.customer_address)
|
||||
else:
|
||||
buyer_details = get_party_details(invoice.customer_address)
|
||||
place_of_supply = get_place_of_supply(invoice, invoice.doctype) or sanitize_for_json(invoice.billing_address_gstin)
|
||||
place_of_supply = place_of_supply[:2]
|
||||
place_of_supply = get_place_of_supply(invoice, invoice.doctype)
|
||||
if place_of_supply:
|
||||
place_of_supply = place_of_supply.split('-')[0]
|
||||
else:
|
||||
place_of_supply = sanitize_for_json(invoice.billing_address_gstin)[:2]
|
||||
buyer_details.update(dict(place_of_supply=place_of_supply))
|
||||
|
||||
shipping_details = payment_details = prev_doc_details = eway_bill_details = frappe._dict({})
|
||||
|
@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
||||
|
||||
import frappe, os, json
|
||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
||||
from frappe.permissions import add_permission, update_permission_property
|
||||
from erpnext.regional.india import states
|
||||
from erpnext.accounts.utils import get_fiscal_year, FiscalYearError
|
||||
@ -18,6 +19,7 @@ def setup(company=None, patch=True):
|
||||
# TODO: for all countries
|
||||
def setup_company_independent_fixtures():
|
||||
make_custom_fields()
|
||||
make_property_setters()
|
||||
add_permissions()
|
||||
add_custom_roles_for_reports()
|
||||
frappe.enqueue('erpnext.regional.india.setup.add_hsn_sac_codes', now=frappe.flags.in_test)
|
||||
@ -110,6 +112,11 @@ def add_print_formats():
|
||||
frappe.db.set_value("Print Format", "GST Tax Invoice", "disabled", 0)
|
||||
frappe.db.set_value("Print Format", "GST E-Invoice", "disabled", 0)
|
||||
|
||||
def make_property_setters():
|
||||
# GST rules do not allow for an invoice no. bigger than 16 characters
|
||||
make_property_setter('Sales Invoice', 'naming_series', 'options', 'SINV-.YY.-\nSRET-.YY.-', '')
|
||||
make_property_setter('Purchase Invoice', 'naming_series', 'options', 'PINV-.YY.-\nPRET-.YY.-', '')
|
||||
|
||||
def make_custom_fields(update=True):
|
||||
hsn_sac_field = dict(fieldname='gst_hsn_code', label='HSN/SAC',
|
||||
fieldtype='Data', fetch_from='item_code.gst_hsn_code', insert_after='description',
|
||||
@ -860,4 +867,4 @@ def create_gratuity_rule():
|
||||
})
|
||||
|
||||
rule.flags.ignore_mandatory = True
|
||||
rule.save()
|
||||
rule.save()
|
||||
|
@ -230,13 +230,20 @@ class Customer(TransactionBase):
|
||||
frappe.db.set(self, "customer_name", newdn)
|
||||
|
||||
def set_loyalty_program(self):
|
||||
if self.loyalty_program: return
|
||||
if self.loyalty_program:
|
||||
return
|
||||
|
||||
loyalty_program = get_loyalty_programs(self)
|
||||
if not loyalty_program: return
|
||||
if not loyalty_program:
|
||||
return
|
||||
|
||||
if len(loyalty_program) == 1:
|
||||
self.loyalty_program = loyalty_program[0]
|
||||
else:
|
||||
frappe.msgprint(_("Multiple Loyalty Program found for the Customer. Please select manually."))
|
||||
frappe.msgprint(
|
||||
_("Multiple Loyalty Programs found for Customer {}. Please select manually.")
|
||||
.format(frappe.bold(self.customer_name))
|
||||
)
|
||||
|
||||
def create_onboarding_docs(self, args):
|
||||
defaults = frappe.defaults.get_defaults()
|
||||
@ -340,7 +347,6 @@ def _set_missing_values(source, target):
|
||||
@frappe.whitelist()
|
||||
def get_loyalty_programs(doc):
|
||||
''' returns applicable loyalty programs for a customer '''
|
||||
from frappe.desk.treeview import get_children
|
||||
|
||||
lp_details = []
|
||||
loyalty_programs = frappe.get_all("Loyalty Program",
|
||||
@ -349,15 +355,33 @@ def get_loyalty_programs(doc):
|
||||
"ifnull(to_date, '2500-01-01')": [">=", today()]})
|
||||
|
||||
for loyalty_program in loyalty_programs:
|
||||
customer_groups = [d.value for d in get_children("Customer Group", loyalty_program.customer_group)] + [loyalty_program.customer_group]
|
||||
customer_territories = [d.value for d in get_children("Territory", loyalty_program.customer_territory)] + [loyalty_program.customer_territory]
|
||||
|
||||
if (not loyalty_program.customer_group or doc.customer_group in customer_groups)\
|
||||
and (not loyalty_program.customer_territory or doc.territory in customer_territories):
|
||||
if (
|
||||
(not loyalty_program.customer_group
|
||||
or doc.customer_group in get_nested_links(
|
||||
"Customer Group",
|
||||
loyalty_program.customer_group,
|
||||
doc.flags.ignore_permissions
|
||||
))
|
||||
and (not loyalty_program.customer_territory
|
||||
or doc.territory in get_nested_links(
|
||||
"Territory",
|
||||
loyalty_program.customer_territory,
|
||||
doc.flags.ignore_permissions
|
||||
))
|
||||
):
|
||||
lp_details.append(loyalty_program.name)
|
||||
|
||||
return lp_details
|
||||
|
||||
def get_nested_links(link_doctype, link_name, ignore_permissions=False):
|
||||
from frappe.desk.treeview import _get_children
|
||||
|
||||
links = [link_name]
|
||||
for d in _get_children(link_doctype, link_name, ignore_permissions):
|
||||
links.append(d.value)
|
||||
|
||||
return links
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None):
|
||||
@ -572,4 +596,4 @@ def get_customer_primary_contact(doctype, txt, searchfield, start, page_len, fil
|
||||
""", {
|
||||
'customer': customer,
|
||||
'txt': '%%%s%%' % txt
|
||||
})
|
||||
})
|
||||
|
@ -150,7 +150,7 @@ class SalesOrder(SellingController):
|
||||
if enq:
|
||||
frappe.db.sql("update `tabOpportunity` set status = %s where name=%s",(flag,enq[0][0]))
|
||||
|
||||
def update_prevdoc_status(self, flag):
|
||||
def update_prevdoc_status(self, flag=None):
|
||||
for quotation in list(set([d.prevdoc_docname for d in self.get("items")])):
|
||||
if quotation:
|
||||
doc = frappe.get_doc("Quotation", quotation)
|
||||
|
@ -341,6 +341,9 @@ class TestSalesOrder(unittest.TestCase):
|
||||
prev_total = so.get("base_total")
|
||||
prev_total_in_words = so.get("base_in_words")
|
||||
|
||||
# get reserved qty before update items
|
||||
reserved_qty_for_second_item = get_reserved_qty("_Test Item 2")
|
||||
|
||||
first_item_of_so = so.get("items")[0]
|
||||
trans_item = json.dumps([
|
||||
{'item_code' : first_item_of_so.item_code, 'rate' : first_item_of_so.rate, \
|
||||
@ -354,6 +357,10 @@ class TestSalesOrder(unittest.TestCase):
|
||||
self.assertEqual(so.get("items")[-1].rate, 200)
|
||||
self.assertEqual(so.get("items")[-1].qty, 7)
|
||||
self.assertEqual(so.get("items")[-1].amount, 1400)
|
||||
|
||||
# reserved qty should increase after adding row
|
||||
self.assertEqual(get_reserved_qty('_Test Item 2'), reserved_qty_for_second_item + 7)
|
||||
|
||||
self.assertEqual(so.status, 'To Deliver and Bill')
|
||||
|
||||
updated_total = so.get("base_total")
|
||||
@ -373,6 +380,9 @@ class TestSalesOrder(unittest.TestCase):
|
||||
create_dn_against_so(so.name, 2)
|
||||
make_sales_invoice(so.name)
|
||||
|
||||
# get reserved qty before update items
|
||||
reserved_qty_for_second_item = get_reserved_qty("_Test Item 2")
|
||||
|
||||
# add an item so as to try removing items
|
||||
trans_item = json.dumps([
|
||||
{"item_code": '_Test Item', "qty": 5, "rate":1000, "docname": so.get("items")[0].name},
|
||||
@ -382,6 +392,9 @@ class TestSalesOrder(unittest.TestCase):
|
||||
so.reload()
|
||||
self.assertEqual(len(so.get("items")), 2)
|
||||
|
||||
# reserved qty should increase after adding row
|
||||
self.assertEqual(get_reserved_qty('_Test Item 2'), reserved_qty_for_second_item + 2)
|
||||
|
||||
# check if delivered items can be removed
|
||||
trans_item = json.dumps([{
|
||||
"item_code": '_Test Item 2',
|
||||
@ -402,6 +415,10 @@ class TestSalesOrder(unittest.TestCase):
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(len(so.get("items")), 1)
|
||||
|
||||
# reserved qty should decrease (back to initial) after deleting row
|
||||
self.assertEqual(get_reserved_qty('_Test Item 2'), reserved_qty_for_second_item)
|
||||
|
||||
self.assertEqual(so.status, 'To Deliver and Bill')
|
||||
|
||||
|
||||
@ -503,12 +520,18 @@ class TestSalesOrder(unittest.TestCase):
|
||||
|
||||
so = make_sales_order(item_code = "_Test Item", warehouse=None)
|
||||
|
||||
# get reserved qty of packed item
|
||||
existing_reserved_qty = get_reserved_qty("_Packed Item")
|
||||
|
||||
added_item = json.dumps([{"item_code" : "_Product Bundle Item", "rate" : 200, 'qty' : 2}])
|
||||
update_child_qty_rate('Sales Order', added_item, so.name)
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(so.packed_items[0].qty, 4)
|
||||
|
||||
# reserved qty in packed item should increase after adding bundle item
|
||||
self.assertEqual(get_reserved_qty("_Packed Item"), existing_reserved_qty + 4)
|
||||
|
||||
# test uom and conversion factor change
|
||||
update_uom_conv_factor = json.dumps([{
|
||||
'item_code': so.get("items")[0].item_code,
|
||||
@ -523,6 +546,9 @@ class TestSalesOrder(unittest.TestCase):
|
||||
so.reload()
|
||||
self.assertEqual(so.packed_items[0].qty, 8)
|
||||
|
||||
# reserved qty in packed item should increase after changing bundle item uom
|
||||
self.assertEqual(get_reserved_qty("_Packed Item"), existing_reserved_qty + 8)
|
||||
|
||||
def test_update_child_with_tax_template(self):
|
||||
"""
|
||||
Test Action: Create a SO with one item having its tax account head already in the SO.
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "MAT-BIN-.YYYY.-.#####",
|
||||
"creation": "2013-01-10 16:34:25",
|
||||
"doctype": "DocType",
|
||||
@ -112,7 +113,8 @@
|
||||
{
|
||||
"fieldname": "reserved_qty_for_sub_contract",
|
||||
"fieldtype": "Float",
|
||||
"label": "Reserved Qty for sub contract"
|
||||
"label": "Reserved Qty for sub contract",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "ma_rate",
|
||||
@ -166,7 +168,8 @@
|
||||
"hide_toolbar": 1,
|
||||
"idx": 1,
|
||||
"in_create": 1,
|
||||
"modified": "2019-11-18 18:34:59.456882",
|
||||
"links": [],
|
||||
"modified": "2021-03-30 23:09:39.572776",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Bin",
|
||||
@ -196,5 +199,6 @@
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"search_fields": "item_code,warehouse",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC"
|
||||
}
|
@ -12,6 +12,7 @@
|
||||
"item_name": "_Test Item",
|
||||
"apply_warehouse_wise_reorder_level": 1,
|
||||
"gst_hsn_code": "999800",
|
||||
"opening_stock": 10,
|
||||
"valuation_rate": 100,
|
||||
"item_defaults": [{
|
||||
"company": "_Test Company",
|
||||
|
@ -354,6 +354,10 @@ frappe.ui.form.on('Material Request', {
|
||||
},
|
||||
material_request_type: function(frm) {
|
||||
frm.toggle_reqd('customer', frm.doc.material_request_type=="Customer Provided");
|
||||
|
||||
if (frm.doc.material_request_type !== 'Material Transfer' && frm.doc.set_from_warehouse) {
|
||||
frm.set_value('set_from_warehouse', '');
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
@ -20,9 +20,9 @@
|
||||
"company",
|
||||
"amended_from",
|
||||
"warehouse_section",
|
||||
"set_warehouse",
|
||||
"column_break5",
|
||||
"set_from_warehouse",
|
||||
"column_break5",
|
||||
"set_warehouse",
|
||||
"items_section",
|
||||
"scan_barcode",
|
||||
"items",
|
||||
@ -314,7 +314,7 @@
|
||||
"idx": 70,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-19 01:04:09.285862",
|
||||
"modified": "2021-03-31 23:52:55.392512",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Material Request",
|
||||
|
@ -64,17 +64,21 @@ class QualityInspection(Document):
|
||||
(quality_inspection, self.modified, self.reference_name, self.item_code))
|
||||
|
||||
else:
|
||||
args = [quality_inspection, self.modified, self.reference_name, self.item_code]
|
||||
doctype = self.reference_type + ' Item'
|
||||
|
||||
if self.reference_type == 'Stock Entry':
|
||||
doctype = 'Stock Entry Detail'
|
||||
|
||||
if self.reference_type and self.reference_name:
|
||||
conditions = ""
|
||||
if self.batch_no and self.docstatus == 1:
|
||||
conditions += " and t1.batch_no = '%s'"%(self.batch_no)
|
||||
conditions += " and t1.batch_no = %s"
|
||||
args.append(self.batch_no)
|
||||
|
||||
if self.docstatus == 2: # if cancel, then remove qi link wherever same name
|
||||
conditions += " and t1.quality_inspection = '%s'"%(self.name)
|
||||
conditions += " and t1.quality_inspection = %s"
|
||||
args.append(self.name)
|
||||
|
||||
frappe.db.sql("""
|
||||
UPDATE
|
||||
@ -87,7 +91,7 @@ class QualityInspection(Document):
|
||||
and t1.parent = t2.name
|
||||
{conditions}
|
||||
""".format(parent_doc=self.reference_type, child_doc=doctype, conditions=conditions),
|
||||
(quality_inspection, self.modified, self.reference_name, self.item_code))
|
||||
args)
|
||||
|
||||
def inspect_and_set_status(self):
|
||||
for reading in self.readings:
|
||||
|
Loading…
x
Reference in New Issue
Block a user