Merge branch 'frappe:develop' into get_incoming_rate_zero_in_rate_fix
diff --git a/CODEOWNERS b/CODEOWNERS
index 7f8c4d1..540680c 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -22,4 +22,4 @@
erpnext/patches/ @deepeshgarg007
.github/ @deepeshgarg007
-pyproject.toml @ankush
+pyproject.toml @phot0n
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index c0eed18..5cecddd 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -184,6 +184,7 @@
},
{
"default": "0",
+ "description": "Payment Terms from orders will be fetched into the invoices as is",
"fieldname": "automatically_fetch_payment_terms",
"fieldtype": "Check",
"label": "Automatically Fetch Payment Terms from Order"
@@ -375,7 +376,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2023-03-28 09:50:20.375233",
+ "modified": "2023-04-14 17:22:03.680886",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index f8a2653..3583dc7 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -1754,7 +1754,12 @@
if doc.doctype == "Purchase Invoice" and doc.invoice_is_blocked():
frappe.msgprint(_("{0} is on hold till {1}").format(doc.name, doc.release_date))
else:
- if doc.doctype in ("Sales Invoice", "Purchase Invoice") and frappe.get_cached_value(
+ if doc.doctype in (
+ "Sales Invoice",
+ "Purchase Invoice",
+ "Purchase Order",
+ "Sales Order",
+ ) and frappe.get_cached_value(
"Payment Terms Template",
doc.payment_terms_template,
"allocate_payment_based_on_payment_terms",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 29afc84..ff08ddd 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -495,6 +495,7 @@
"allow_bulk_edit": 1,
"fieldname": "items",
"fieldtype": "Table",
+ "label": "Items",
"oldfieldname": "po_details",
"oldfieldtype": "Table",
"options": "Purchase Order Item",
@@ -1100,8 +1101,7 @@
{
"fieldname": "before_items_section",
"fieldtype": "Section Break",
- "hide_border": 1,
- "label": "Items"
+ "hide_border": 1
},
{
"fieldname": "items_col_break",
@@ -1271,7 +1271,7 @@
"idx": 105,
"is_submittable": 1,
"links": [],
- "modified": "2023-01-28 18:59:16.322824",
+ "modified": "2023-04-14 16:42:29.448464",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index c5b369b..11ff91a 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -310,7 +310,6 @@
"fieldname": "items_section",
"fieldtype": "Section Break",
"hide_border": 1,
- "label": "Items",
"oldfieldtype": "Section Break",
"options": "fa fa-shopping-cart"
},
@@ -318,6 +317,7 @@
"allow_bulk_edit": 1,
"fieldname": "items",
"fieldtype": "Table",
+ "label": "Items",
"oldfieldname": "po_details",
"oldfieldtype": "Table",
"options": "Supplier Quotation Item",
@@ -844,7 +844,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2022-12-12 18:35:39.740974",
+ "modified": "2023-04-14 16:43:41.714832",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation",
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 7fcc28b..c741622 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -273,8 +273,8 @@
self.validate_payment_schedule_dates()
self.set_due_date()
self.set_payment_schedule()
- self.validate_payment_schedule_amount()
if not self.get("ignore_default_payment_terms_template"):
+ self.validate_payment_schedule_amount()
self.validate_due_date()
self.validate_advance_entries()
@@ -1607,6 +1607,7 @@
base_grand_total = self.get("base_rounded_total") or self.base_grand_total
grand_total = self.get("rounded_total") or self.grand_total
+ automatically_fetch_payment_terms = 0
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
base_grand_total = base_grand_total - flt(self.base_write_off_amount)
@@ -1652,19 +1653,20 @@
)
self.append("payment_schedule", data)
- for d in self.get("payment_schedule"):
- if d.invoice_portion:
- d.payment_amount = flt(
- grand_total * flt(d.invoice_portion / 100), d.precision("payment_amount")
- )
- d.base_payment_amount = flt(
- base_grand_total * flt(d.invoice_portion / 100), d.precision("base_payment_amount")
- )
- d.outstanding = d.payment_amount
- elif not d.invoice_portion:
- d.base_payment_amount = flt(
- d.payment_amount * self.get("conversion_rate"), d.precision("base_payment_amount")
- )
+ if not automatically_fetch_payment_terms:
+ for d in self.get("payment_schedule"):
+ if d.invoice_portion:
+ d.payment_amount = flt(
+ grand_total * flt(d.invoice_portion / 100), d.precision("payment_amount")
+ )
+ d.base_payment_amount = flt(
+ base_grand_total * flt(d.invoice_portion / 100), d.precision("base_payment_amount")
+ )
+ d.outstanding = d.payment_amount
+ elif not d.invoice_portion:
+ d.base_payment_amount = flt(
+ d.payment_amount * self.get("conversion_rate"), d.precision("base_payment_amount")
+ )
def get_order_details(self):
if self.doctype == "Sales Invoice":
@@ -1717,6 +1719,10 @@
"invoice_portion": schedule.invoice_portion,
"mode_of_payment": schedule.mode_of_payment,
"description": schedule.description,
+ "payment_amount": schedule.payment_amount,
+ "base_payment_amount": schedule.base_payment_amount,
+ "outstanding": schedule.outstanding,
+ "paid_amount": schedule.paid_amount,
}
if schedule.discount_type == "Percentage":
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 1e4fabe..479fef7 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -859,6 +859,8 @@
def future_sle_exists(args, sl_entries=None):
key = (args.voucher_type, args.voucher_no)
+ if not hasattr(frappe.local, "future_sle"):
+ frappe.local.future_sle = {}
if validate_future_sle_not_exists(args, key, sl_entries):
return False
@@ -892,6 +894,9 @@
)
for d in data:
+ if key not in frappe.local.future_sle:
+ frappe.local.future_sle[key] = frappe._dict({})
+
frappe.local.future_sle[key][(d.item_code, d.warehouse)] = d.total_row
return len(data)
@@ -903,6 +908,9 @@
item_key = (args.get("item_code"), args.get("warehouse"))
if not sl_entries and hasattr(frappe.local, "future_sle"):
+ if key not in frappe.local.future_sle:
+ return False
+
if not frappe.local.future_sle.get(key) or (
item_key and item_key not in frappe.local.future_sle.get(key)
):
@@ -910,11 +918,8 @@
def get_cached_data(args, key):
- if not hasattr(frappe.local, "future_sle"):
- frappe.local.future_sle = {}
-
if key not in frappe.local.future_sle:
- frappe.local.future_sle[key] = frappe._dict({})
+ return False
if args.get("item_code"):
item_key = (args.get("item_code"), args.get("warehouse"))
diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json
index 077e7fa..0cb8824 100644
--- a/erpnext/crm/doctype/lead/lead.json
+++ b/erpnext/crm/doctype/lead/lead.json
@@ -2,6 +2,7 @@
"actions": [],
"allow_events_in_timeline": 1,
"allow_import": 1,
+ "allow_rename": 1,
"autoname": "naming_series:",
"creation": "2022-02-08 13:14:41.083327",
"doctype": "DocType",
@@ -515,7 +516,7 @@
"idx": 5,
"image_field": "image",
"links": [],
- "modified": "2023-01-24 18:20:05.044791",
+ "modified": "2023-04-14 18:20:05.044791",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead",
@@ -582,4 +583,4 @@
"states": [],
"subject_field": "title",
"title_field": "title"
-}
\ No newline at end of file
+}
diff --git a/erpnext/e_commerce/doctype/website_item/website_item.py b/erpnext/e_commerce/doctype/website_item/website_item.py
index 3e5d5f7..81b8eca 100644
--- a/erpnext/e_commerce/doctype/website_item/website_item.py
+++ b/erpnext/e_commerce/doctype/website_item/website_item.py
@@ -315,6 +315,7 @@
self.item_code, skip_quotation_creation=True
)
+ @frappe.whitelist()
def copy_specification_from_item_group(self):
self.set("website_specifications", [])
if self.item_group:
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 0bd4d91..1675e2c 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1920,7 +1920,7 @@
}
prompt_user_for_reference_date(){
- var me = this;
+ let me = this;
frappe.prompt({
label: __("Cheque/Reference Date"),
fieldname: "reference_date",
@@ -1947,7 +1947,7 @@
let has_payment_schedule = this.frm.doc.payment_schedule && this.frm.doc.payment_schedule.length;
if(!is_eligible || !has_payment_schedule) return false;
- let has_discount = this.frm.doc.payment_schedule.some(row => row.discount_date);
+ let has_discount = this.frm.doc.payment_schedule.some(row => row.discount);
return has_discount;
}
diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json
index eb2c0a4..2ffa6a5 100644
--- a/erpnext/selling/doctype/quotation/quotation.json
+++ b/erpnext/selling/doctype/quotation/quotation.json
@@ -416,7 +416,6 @@
"fieldname": "items_section",
"fieldtype": "Section Break",
"hide_border": 1,
- "label": "Items",
"oldfieldtype": "Section Break",
"options": "fa fa-shopping-cart"
},
@@ -424,6 +423,7 @@
"allow_bulk_edit": 1,
"fieldname": "items",
"fieldtype": "Table",
+ "label": "Items",
"oldfieldname": "quotation_details",
"oldfieldtype": "Table",
"options": "Quotation Item",
@@ -1072,7 +1072,7 @@
"idx": 82,
"is_submittable": 1,
"links": [],
- "modified": "2022-12-12 18:32:28.671332",
+ "modified": "2023-04-14 16:50:44.550098",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation",
diff --git a/erpnext/selling/workspace/selling/selling.json b/erpnext/selling/workspace/selling/selling.json
index 45e160d..180a3d7 100644
--- a/erpnext/selling/workspace/selling/selling.json
+++ b/erpnext/selling/workspace/selling/selling.json
@@ -704,7 +704,7 @@
"type": "Link"
}
],
- "modified": "2022-04-26 13:29:55.087240",
+ "modified": "2023-04-16 13:29:55.087240",
"modified_by": "Administrator",
"module": "Selling",
"name": "Selling",
diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py
index 088958d..3e1e394 100644
--- a/erpnext/setup/install.py
+++ b/erpnext/setup/install.py
@@ -161,7 +161,7 @@
{
"item_label": "User Forum",
"item_type": "Route",
- "route": "https://discuss.erpnext.com",
+ "route": "https://discuss.frappe.io",
"is_standard": 1,
},
{
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index e304bd1..3fd4cec 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -571,24 +571,33 @@
self._cancel()
def recalculate_current_qty(self, item_code, batch_no):
+ from erpnext.stock.stock_ledger import get_valuation_rate
+
+ sl_entries = []
for row in self.items:
if not (row.item_code == item_code and row.batch_no == batch_no):
continue
- row.current_qty = get_batch_qty_for_stock_reco(
+ current_qty = get_batch_qty_for_stock_reco(
item_code, row.warehouse, batch_no, self.posting_date, self.posting_time, self.name
)
- qty, val_rate = get_stock_balance(
- item_code,
- row.warehouse,
- self.posting_date,
- self.posting_time,
- with_valuation_rate=True,
+ precesion = row.precision("current_qty")
+ if flt(current_qty, precesion) == flt(row.current_qty, precesion):
+ continue
+
+ val_rate = get_valuation_rate(
+ item_code, row.warehouse, self.doctype, self.name, company=self.company, batch_no=batch_no
)
row.current_valuation_rate = val_rate
+ if not row.current_qty and current_qty:
+ sle = self.get_sle_for_items(row)
+ sle.actual_qty = current_qty * -1
+ sle.valuation_rate = val_rate
+ sl_entries.append(sle)
+ row.current_qty = current_qty
row.db_set(
{
"current_qty": row.current_qty,
@@ -597,6 +606,9 @@
}
)
+ if sl_entries:
+ self.make_sl_entries(sl_entries)
+
def get_batch_qty_for_stock_reco(
item_code, warehouse, batch_no, posting_date, posting_time, voucher_no
diff --git a/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js b/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js
index 58a043e..752e464 100644
--- a/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js
+++ b/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js
@@ -11,6 +11,13 @@
"options": "Company",
"reqd": 1,
"default": frappe.defaults.get_user_default("Company")
+ },
+ {
+ "fieldname":"show_disabled_warehouses",
+ "label": __("Show Disabled Warehouses"),
+ "fieldtype": "Check",
+ "default": 0
+
}
],
"initial_depth": 3,
diff --git a/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py b/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py
index d364b57..a0e9944 100644
--- a/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py
+++ b/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.py
@@ -11,6 +11,7 @@
class StockBalanceFilter(TypedDict):
company: Optional[str]
warehouse: Optional[str]
+ show_disabled_warehouses: Optional[int]
SLEntry = Dict[str, Any]
@@ -18,7 +19,7 @@
def execute(filters=None):
columns, data = [], []
- columns = get_columns()
+ columns = get_columns(filters)
data = get_data(filters)
return columns, data
@@ -42,10 +43,14 @@
def get_warehouses(report_filters: StockBalanceFilter):
+ filters = {"company": report_filters.company, "disabled": 0}
+ if report_filters.get("show_disabled_warehouses"):
+ filters["disabled"] = ("in", [0, report_filters.show_disabled_warehouses])
+
return frappe.get_all(
"Warehouse",
- fields=["name", "parent_warehouse", "is_group"],
- filters={"company": report_filters.company},
+ fields=["name", "parent_warehouse", "is_group", "disabled"],
+ filters=filters,
order_by="lft",
)
@@ -90,8 +95,8 @@
update_balance(warehouse, warehouse.stock_balance)
-def get_columns():
- return [
+def get_columns(filters: StockBalanceFilter) -> List[Dict]:
+ columns = [
{
"label": _("Warehouse"),
"fieldname": "name",
@@ -101,3 +106,15 @@
},
{"label": _("Stock Balance"), "fieldname": "stock_balance", "fieldtype": "Float", "width": 150},
]
+
+ if filters.get("show_disabled_warehouses"):
+ columns.append(
+ {
+ "label": _("Warehouse Disabled?"),
+ "fieldname": "disabled",
+ "fieldtype": "Check",
+ "width": 200,
+ }
+ )
+
+ return columns
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index b0a093d..b638f08 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -545,6 +545,14 @@
self.get_dynamic_incoming_outgoing_rate(sle)
if (
+ sle.voucher_type == "Stock Reconciliation"
+ and sle.batch_no
+ and sle.voucher_detail_no
+ and sle.actual_qty < 0
+ ):
+ self.reset_actual_qty_for_stock_reco(sle)
+
+ if (
sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
and sle.voucher_detail_no
and sle.actual_qty < 0
@@ -605,6 +613,16 @@
if not self.args.get("sle_id"):
self.update_outgoing_rate_on_transaction(sle)
+ def reset_actual_qty_for_stock_reco(self, sle):
+ current_qty = frappe.get_cached_value(
+ "Stock Reconciliation Item", sle.voucher_detail_no, "current_qty"
+ )
+
+ if current_qty:
+ sle.actual_qty = current_qty * -1
+ elif current_qty == 0:
+ sle.is_cancelled = 1
+
def validate_negative_stock(self, sle):
"""
validate negative stock for entries current datetime onwards
@@ -1369,12 +1387,7 @@
def regenerate_sle_for_batch_stock_reco(detail):
doc = frappe.get_cached_doc("Stock Reconciliation", detail.voucher_no)
- doc.docstatus = 2
- doc.update_stock_ledger()
-
doc.recalculate_current_qty(detail.item_code, detail.batch_no)
- doc.docstatus = 1
- doc.update_stock_ledger()
doc.repost_future_sle_and_gle()
@@ -1401,34 +1414,52 @@
return stock_reco_qty_shift
-def get_next_stock_reco(args):
+def get_next_stock_reco(kwargs):
"""Returns next nearest stock reconciliaton's details."""
- return frappe.db.sql(
- """
- select
- name, posting_date, posting_time, creation, voucher_no, item_code, batch_no, actual_qty
- from
- `tabStock Ledger Entry`
- where
- item_code = %(item_code)s
- and warehouse = %(warehouse)s
- and voucher_type = 'Stock Reconciliation'
- and voucher_no != %(voucher_no)s
- and is_cancelled = 0
- and (timestamp(posting_date, posting_time) > timestamp(%(posting_date)s, %(posting_time)s)
- or (
- timestamp(posting_date, posting_time) = timestamp(%(posting_date)s, %(posting_time)s)
- and creation > %(creation)s
+ sle = frappe.qb.DocType("Stock Ledger Entry")
+
+ query = (
+ frappe.qb.from_(sle)
+ .select(
+ sle.name,
+ sle.posting_date,
+ sle.posting_time,
+ sle.creation,
+ sle.voucher_no,
+ sle.item_code,
+ sle.batch_no,
+ sle.actual_qty,
+ )
+ .where(
+ (sle.item_code == kwargs.get("item_code"))
+ & (sle.warehouse == kwargs.get("warehouse"))
+ & (sle.voucher_type == "Stock Reconciliation")
+ & (sle.voucher_no != kwargs.get("voucher_no"))
+ & (sle.is_cancelled == 0)
+ & (
+ (
+ CombineDatetime(sle.posting_date, sle.posting_time)
+ > CombineDatetime(kwargs.get("posting_date"), kwargs.get("posting_time"))
+ | (
+ (
+ CombineDatetime(sle.posting_date, sle.posting_time)
+ == CombineDatetime(kwargs.get("posting_date"), kwargs.get("posting_time"))
+ )
+ & (sle.creation > kwargs.get("creation"))
+ )
)
)
- order by timestamp(posting_date, posting_time) asc, creation asc
- limit 1
- """,
- args,
- as_dict=1,
+ )
+ .orderby(CombineDatetime(sle.posting_date, sle.posting_time))
+ .orderby(sle.creation)
)
+ if kwargs.get("batch_no"):
+ query.where(sle.batch_no == kwargs.get("batch_no"))
+
+ return query.run(as_dict=True)
+
def get_datetime_limit_condition(detail):
return f"""
diff --git a/erpnext/templates/generators/item/item_add_to_cart.html b/erpnext/templates/generators/item/item_add_to_cart.html
index 8000a24..1381dfe 100644
--- a/erpnext/templates/generators/item/item_add_to_cart.html
+++ b/erpnext/templates/generators/item/item_add_to_cart.html
@@ -11,7 +11,10 @@
<div class="product-price">
<!-- Final Price -->
- {{ price_info.formatted_price_sales_uom }}
+ <span itemprop="offers" itemscope itemtype="https://schema.org/Offer">
+ <span itemprop="price" content="{{ price_info.price_list_rate }}">{{ price_info.formatted_price_sales_uom }}</span>
+ <span style="display:none;" itemprop="priceCurrency" content="{{ price_info.currency }}">{{ price_info.currency }}</span>
+ </span>
<!-- Striked Price and Discount -->
{% if price_info.formatted_mrp %}