Merge branch 'develop' into fix/github-issue/28766
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 79fab64..26192ec 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -1758,6 +1758,8 @@
pe.setup_party_account_field()
pe.set_missing_values()
+ update_accounting_dimensions(pe, doc)
+
if party_account and bank:
pe.set_exchange_rate(ref_doc=reference_doc)
pe.set_amounts()
@@ -1775,6 +1777,18 @@
return pe
+def update_accounting_dimensions(pe, doc):
+ """
+ Updates accounting dimensions in Payment Entry based on the accounting dimensions in the reference document
+ """
+ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+ )
+
+ for dimension in get_accounting_dimensions():
+ pe.set(dimension, doc.get(dimension))
+
+
def get_bank_cash_account(doc, bank_account):
bank = get_default_bank_cash_account(
doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"), account=bank_account
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json
index 2f3516e..381f3fb 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.json
+++ b/erpnext/accounts/doctype/payment_request/payment_request.json
@@ -32,6 +32,10 @@
"iban",
"branch_code",
"swift_number",
+ "accounting_dimensions_section",
+ "cost_center",
+ "dimension_col_break",
+ "project",
"recipient_and_message",
"print_format",
"email_to",
@@ -362,13 +366,35 @@
"label": "Payment Channel",
"options": "\nEmail\nPhone",
"read_only": 1
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "accounting_dimensions_section",
+ "fieldtype": "Section Break",
+ "label": "Accounting Dimensions"
+ },
+ {
+ "fieldname": "cost_center",
+ "fieldtype": "Link",
+ "label": "Cost Center",
+ "options": "Cost Center"
+ },
+ {
+ "fieldname": "dimension_col_break",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "label": "Project",
+ "options": "Project"
}
],
"in_create": 1,
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2022-09-30 16:19:43.680025",
+ "modified": "2022-12-21 16:56:40.115737",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Request",
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index fc93801..4fc12db 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -10,6 +10,9 @@
from frappe.utils import flt, get_url, nowdate
from frappe.utils.background_jobs import enqueue
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+ get_accounting_dimensions,
+)
from erpnext.accounts.doctype.payment_entry.payment_entry import (
get_company_defaults,
get_payment_entry,
@@ -270,6 +273,17 @@
}
)
+ # Update dimensions
+ payment_entry.update(
+ {
+ "cost_center": self.get("cost_center"),
+ "project": self.get("project"),
+ }
+ )
+
+ for dimension in get_accounting_dimensions():
+ payment_entry.update({dimension: self.get(dimension)})
+
if payment_entry.difference_amount:
company_details = get_company_defaults(ref_doc.company)
@@ -449,6 +463,17 @@
}
)
+ # Update dimensions
+ pr.update(
+ {
+ "cost_center": ref_doc.get("cost_center"),
+ "project": ref_doc.get("project"),
+ }
+ )
+
+ for dimension in get_accounting_dimensions():
+ pr.update({dimension: ref_doc.get(dimension)})
+
if args.order_type == "Shopping Cart" or args.mute_email:
pr.flags.mute_email = True
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index 3989f8a..1ce780e 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -252,10 +252,15 @@
if args.get("doctype") in [
"Quotation",
+ "Quotation Item",
"Sales Order",
+ "Sales Order Item",
"Delivery Note",
+ "Delivery Note Item",
"Sales Invoice",
+ "Sales Invoice Item",
"POS Invoice",
+ "POS Invoice Item",
]:
conditions += """ and ifnull(`tabPricing Rule`.selling, 0) = 1"""
else:
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index 62c3ced..35d19ed 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -890,7 +890,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2022-11-02 12:53:12.693217",
+ "modified": "2022-12-28 16:17:33.484531",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
index c04b9c7..d34c213 100644
--- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
+++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
@@ -53,9 +53,6 @@
item_details = get_item_details()
for d in item_list:
- if not d.stock_qty:
- continue
-
item_record = item_details.get(d.item_code)
purchase_receipt = None
@@ -94,7 +91,7 @@
"expense_account": expense_account,
"stock_qty": d.stock_qty,
"stock_uom": d.stock_uom,
- "rate": d.base_net_amount / d.stock_qty,
+ "rate": d.base_net_amount / d.stock_qty if d.stock_qty else d.base_net_amount,
"amount": d.base_net_amount,
}
)
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 4c10b48..5a4168a 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -207,31 +207,36 @@
)
def validate_fg_item_for_subcontracting(self):
- if self.is_subcontracted and not self.is_old_subcontracting_flow:
+ if self.is_subcontracted:
+ if not self.is_old_subcontracting_flow:
+ for item in self.items:
+ if not item.fg_item:
+ frappe.throw(
+ _("Row #{0}: Finished Good Item is not specified for service item {1}").format(
+ item.idx, item.item_code
+ )
+ )
+ else:
+ if not frappe.get_value("Item", item.fg_item, "is_sub_contracted_item"):
+ frappe.throw(
+ _(
+ "Row #{0}: Finished Good Item {1} must be a sub-contracted item for service item {2}"
+ ).format(item.idx, item.fg_item, item.item_code)
+ )
+ elif not frappe.get_value("Item", item.fg_item, "default_bom"):
+ frappe.throw(
+ _("Row #{0}: Default BOM not found for FG Item {1}").format(item.idx, item.fg_item)
+ )
+ if not item.fg_item_qty:
+ frappe.throw(
+ _("Row #{0}: Finished Good Item Qty is not specified for service item {0}").format(
+ item.idx, item.item_code
+ )
+ )
+ else:
for item in self.items:
- if not item.fg_item:
- frappe.throw(
- _("Row #{0}: Finished Good Item is not specified for service item {1}").format(
- item.idx, item.item_code
- )
- )
- else:
- if not frappe.get_value("Item", item.fg_item, "is_sub_contracted_item"):
- frappe.throw(
- _(
- "Row #{0}: Finished Good Item {1} must be a sub-contracted item for service item {2}"
- ).format(item.idx, item.fg_item, item.item_code)
- )
- elif not frappe.get_value("Item", item.fg_item, "default_bom"):
- frappe.throw(
- _("Row #{0}: Default BOM not found for FG Item {1}").format(item.idx, item.fg_item)
- )
- if not item.fg_item_qty:
- frappe.throw(
- _("Row #{0}: Finished Good Item Qty is not specified for service item {0}").format(
- item.idx, item.item_code
- )
- )
+ item.set("fg_item", None)
+ item.set("fg_item_qty", 0)
def get_schedule_dates(self):
for d in self.get("items"):
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 334a2d8..788dc49 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -584,7 +584,12 @@
if bool(uom) != bool(stock_uom): # xor
item.stock_uom = item.uom = uom or stock_uom
- item.conversion_factor = get_uom_conv_factor(item.get("uom"), item.get("stock_uom"))
+ # UOM cannot be zero so substitute as 1
+ item.conversion_factor = (
+ get_uom_conv_factor(item.get("uom"), item.get("stock_uom"))
+ or item.get("conversion_factor")
+ or 1
+ )
if self.doctype == "Purchase Invoice":
self.set_expense_account(for_validate)
diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json
index 72f47b5..0d42ca8 100644
--- a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json
+++ b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"creation": "2019-05-21 07:41:53.536536",
"doctype": "DocType",
"engine": "InnoDB",
@@ -7,10 +8,14 @@
"section_break_2",
"account_sid",
"api_key",
- "api_token"
+ "api_token",
+ "section_break_6",
+ "map_custom_field_to_doctype",
+ "target_doctype"
],
"fields": [
{
+ "default": "0",
"fieldname": "enabled",
"fieldtype": "Check",
"label": "Enabled"
@@ -18,7 +23,8 @@
{
"depends_on": "enabled",
"fieldname": "section_break_2",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Credentials"
},
{
"fieldname": "account_sid",
@@ -34,10 +40,31 @@
"fieldname": "api_key",
"fieldtype": "Data",
"label": "API Key"
+ },
+ {
+ "depends_on": "enabled",
+ "fieldname": "section_break_6",
+ "fieldtype": "Section Break",
+ "label": "Custom Field"
+ },
+ {
+ "default": "0",
+ "fieldname": "map_custom_field_to_doctype",
+ "fieldtype": "Check",
+ "label": "Map Custom Field to DocType"
+ },
+ {
+ "depends_on": "map_custom_field_to_doctype",
+ "fieldname": "target_doctype",
+ "fieldtype": "Link",
+ "label": "Target DocType",
+ "mandatory_depends_on": "map_custom_field_to_doctype",
+ "options": "DocType"
}
],
"issingle": 1,
- "modified": "2019-05-22 06:25:18.026997",
+ "links": [],
+ "modified": "2022-12-14 17:24:50.176107",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "Exotel Settings",
@@ -57,5 +84,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "ASC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/exotel_integration.py b/erpnext/erpnext_integrations/exotel_integration.py
index fd0f783..0d40667 100644
--- a/erpnext/erpnext_integrations/exotel_integration.py
+++ b/erpnext/erpnext_integrations/exotel_integration.py
@@ -72,6 +72,24 @@
return frappe.get_doc("Call Log", call_log_id)
+def map_custom_field(call_payload, call_log):
+ field_value = call_payload.get("CustomField")
+
+ if not field_value:
+ return call_log
+
+ settings = get_exotel_settings()
+ target_doctype = settings.target_doctype
+ mapping_enabled = settings.map_custom_field_to_doctype
+
+ if not mapping_enabled or not target_doctype:
+ return call_log
+
+ call_log.append("links", {"link_doctype": target_doctype, "link_name": field_value})
+
+ return call_log
+
+
def create_call_log(call_payload):
call_log = frappe.new_doc("Call Log")
call_log.id = call_payload.get("CallSid")
@@ -79,6 +97,7 @@
call_log.medium = call_payload.get("To")
call_log.status = "Ringing"
setattr(call_log, "from", call_payload.get("CallFrom"))
+ map_custom_field(call_payload, call_log)
call_log.save(ignore_permissions=True)
frappe.db.commit()
return call_log
@@ -93,10 +112,10 @@
@frappe.whitelist()
-def make_a_call(from_number, to_number, caller_id):
+def make_a_call(from_number, to_number, caller_id, **kwargs):
endpoint = get_exotel_endpoint("Calls/connect.json?details=true")
response = requests.post(
- endpoint, data={"From": from_number, "To": to_number, "CallerId": caller_id}
+ endpoint, data={"From": from_number, "To": to_number, "CallerId": caller_id, **kwargs}
)
return response.json()
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 0aad1d3..2420a23 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -318,4 +318,5 @@
erpnext.patches.v14_0.create_accounting_dimensions_for_asset_capitalization
erpnext.patches.v14_0.update_partial_tds_fields
erpnext.patches.v14_0.create_incoterms_and_migrate_shipment
-erpnext.patches.v14_0.setup_clear_repost_logs
\ No newline at end of file
+erpnext.patches.v14_0.setup_clear_repost_logs
+erpnext.patches.v14_0.create_accounting_dimensions_for_payment_request
\ No newline at end of file
diff --git a/erpnext/patches/v14_0/create_accounting_dimensions_for_payment_request.py b/erpnext/patches/v14_0/create_accounting_dimensions_for_payment_request.py
new file mode 100644
index 0000000..bede419
--- /dev/null
+++ b/erpnext/patches/v14_0/create_accounting_dimensions_for_payment_request.py
@@ -0,0 +1,31 @@
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+
+
+def execute():
+ accounting_dimensions = frappe.db.get_all(
+ "Accounting Dimension", fields=["fieldname", "label", "document_type", "disabled"]
+ )
+
+ if not accounting_dimensions:
+ return
+
+ doctype = "Payment Request"
+
+ for d in accounting_dimensions:
+ field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname})
+
+ if field:
+ continue
+
+ df = {
+ "fieldname": d.fieldname,
+ "label": d.label,
+ "fieldtype": "Link",
+ "options": d.document_type,
+ "insert_after": "accounting_dimensions_section",
+ }
+
+ create_custom_field(doctype, df, ignore_validate=True)
+
+ frappe.clear_cache(doctype=doctype)
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 12ecb01..d9dab33 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -737,7 +737,7 @@
qb.from_(con)
.join(dlink)
.on(con.name == dlink.parent)
- .select(con.name, con.full_name, con.email_id)
+ .select(con.name, con.email_id)
.where((dlink.link_name == customer) & (con.name.like(f"%{txt}%")))
.run()
)
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index efe1f8e..7c0601e 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -1024,6 +1024,15 @@
]
items_to_map = list(set(items_to_map))
+ def is_drop_ship_order(target):
+ drop_ship = True
+ for item in target.items:
+ if not item.delivered_by_supplier:
+ drop_ship = False
+ break
+
+ return drop_ship
+
def set_missing_values(source, target):
target.supplier = ""
target.apply_discount_on = ""
@@ -1031,6 +1040,14 @@
target.discount_amount = 0.0
target.inter_company_order_reference = ""
target.shipping_rule = ""
+
+ if is_drop_ship_order(target):
+ target.customer = source.customer
+ target.customer_name = source.customer_name
+ target.shipping_address = source.shipping_address_name
+ else:
+ target.customer = target.customer_name = target.shipping_address = None
+
target.run_method("set_missing_values")
target.run_method("calculate_taxes_and_totals")
@@ -1057,11 +1074,9 @@
"contact_email",
"contact_person",
"taxes_and_charges",
+ "shipping_address",
"terms",
],
- "field_map": [
- ["shipping_address_name", "shipping_address"],
- ],
"validation": {"docstatus": ["=", 1]},
},
"Sales Order Item": {
diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js
index 6e7622c..1be528f 100644
--- a/erpnext/stock/dashboard/item_dashboard.js
+++ b/erpnext/stock/dashboard/item_dashboard.js
@@ -102,6 +102,9 @@
args: args,
callback: function (r) {
me.render(r.message);
+ if(me.after_refresh) {
+ me.after_refresh();
+ }
}
});
}
diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js
index 799406c..8213adb 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.js
+++ b/erpnext/stock/doctype/pick_list/pick_list.js
@@ -51,7 +51,15 @@
if (!(frm.doc.locations && frm.doc.locations.length)) {
frappe.msgprint(__('Add items in the Item Locations table'));
} else {
- frm.call('set_item_locations', {save: save});
+ frappe.call({
+ method: "set_item_locations",
+ doc: frm.doc,
+ args: {
+ "save": save,
+ },
+ freeze: 1,
+ freeze_message: __("Setting Item Locations..."),
+ });
}
},
get_item_locations: (frm) => {
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index aff5e05..953fca7 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -135,6 +135,7 @@
# reset
self.delete_key("locations")
+ updated_locations = frappe._dict()
for item_doc in items:
item_code = item_doc.item_code
@@ -155,7 +156,26 @@
for row in locations:
location = item_doc.as_dict()
location.update(row)
- self.append("locations", location)
+ key = (
+ location.item_code,
+ location.warehouse,
+ location.uom,
+ location.batch_no,
+ location.serial_no,
+ location.sales_order_item or location.material_request_item,
+ )
+
+ if key not in updated_locations:
+ updated_locations.setdefault(key, location)
+ else:
+ updated_locations[key].qty += location.qty
+ updated_locations[key].stock_qty += location.stock_qty
+
+ for location in updated_locations.values():
+ if location.picked_qty > location.stock_qty:
+ location.picked_qty = location.stock_qty
+
+ self.append("locations", location)
# If table is empty on update after submit, set stock_qty, picked_qty to 0 so that indicator is red
# and give feedback to the user. This is to avoid empty Pick Lists.
@@ -441,7 +461,7 @@
sle.`batch_no`,
sle.`item_code`
HAVING `qty` > 0
- ORDER BY IFNULL(batch.`expiry_date`, '2200-01-01'), batch.`creation`
+ ORDER BY IFNULL(batch.`expiry_date`, '2200-01-01'), batch.`creation`, sle.`batch_no`, sle.`warehouse`
""".format(
warehouse_condition=warehouse_condition
),
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index b910244..d4b4efa 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -112,6 +112,10 @@
}
});
attach_bom_items(frm.doc.bom_no);
+
+ if(!check_should_not_attach_bom_items(frm.doc.bom_no)) {
+ erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
+ }
},
setup_quality_inspection: function(frm) {
@@ -326,7 +330,11 @@
}
frm.trigger("setup_quality_inspection");
- attach_bom_items(frm.doc.bom_no)
+ attach_bom_items(frm.doc.bom_no);
+
+ if(!check_should_not_attach_bom_items(frm.doc.bom_no)) {
+ erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
+ }
},
before_save: function(frm) {
@@ -939,7 +947,10 @@
method: "get_items",
callback: function(r) {
if(!r.exc) refresh_field("items");
- if(me.frm.doc.bom_no) attach_bom_items(me.frm.doc.bom_no)
+ if(me.frm.doc.bom_no) {
+ attach_bom_items(me.frm.doc.bom_no);
+ erpnext.accounts.dimensions.update_dimension(me.frm, me.frm.doctype);
+ }
}
});
}