Merge branch 'develop' into fixed-transferred-materials-are-not-consumed
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index a10a810..f7a57bb 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -1444,7 +1444,7 @@
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
supplier_condition = ""
if voucher_type == "Purchase Invoice":
- supplier_condition = "and (release_date is null or release_date <= CURDATE())"
+ supplier_condition = "and (release_date is null or release_date <= CURRENT_DATE)"
if party_account_currency == company_currency:
grand_total_field = "base_grand_total"
rounded_total_field = "base_rounded_total"
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 2438f4b..98e0a9b 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -36,8 +36,12 @@
def validate_duplicate_apply_on(self):
if self.apply_on != "Transaction":
- field = apply_on_dict.get(self.apply_on)
- values = [d.get(frappe.scrub(self.apply_on)) for d in self.get(field) if field]
+ apply_on_table = apply_on_dict.get(self.apply_on)
+ if not apply_on_table:
+ return
+
+ apply_on_field = frappe.scrub(self.apply_on)
+ values = [d.get(apply_on_field) for d in self.get(apply_on_table) if d.get(apply_on_field)]
if len(values) != len(set(values)):
frappe.throw(_("Duplicate {0} found in the table").format(self.apply_on))
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 3c70e24..6412da7 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -1616,6 +1616,26 @@
company.enable_provisional_accounting_for_non_stock_items = 0
company.save()
+ def test_item_less_defaults(self):
+
+ pi = frappe.new_doc("Purchase Invoice")
+ pi.supplier = "_Test Supplier"
+ pi.company = "_Test Company"
+ pi.append(
+ "items",
+ {
+ "item_name": "Opening item",
+ "qty": 1,
+ "uom": "Tonne",
+ "stock_uom": "Kg",
+ "rate": 1000,
+ "expense_account": "Stock Received But Not Billed - _TC",
+ },
+ )
+
+ pi.save()
+ self.assertEqual(pi.items[0].conversion_factor, 1000)
+
def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
gl_entries = frappe.db.sql(
diff --git a/erpnext/accounts/module_onboarding/accounts/accounts.json b/erpnext/accounts/module_onboarding/accounts/accounts.json
index b9040e3..9916d16 100644
--- a/erpnext/accounts/module_onboarding/accounts/accounts.json
+++ b/erpnext/accounts/module_onboarding/accounts/accounts.json
@@ -13,7 +13,7 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts",
"idx": 0,
"is_complete": 0,
- "modified": "2022-06-07 14:29:21.352132",
+ "modified": "2022-06-14 17:38:24.967834",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts",
diff --git a/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json
index b6e9f5c..e323f6c 100644
--- a/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json
+++ b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json
@@ -2,14 +2,14 @@
"action": "Create Entry",
"action_label": "Manage Sales Tax Templates",
"creation": "2020-05-13 19:29:43.844463",
- "description": "# Setting up Taxes\n\nERPNext lets you configure your taxes so that they are automatically applied in your buying and selling transactions. You can configure them globally or even on Items. ERPNext taxes are pre-configured for most regions.\n\n[Checkout pre-configured taxes](/app/sales-taxes-and-charges-template)\n",
+ "description": "# Setting up Taxes\n\nERPNext lets you configure your taxes so that they are automatically applied in your buying and selling transactions. You can configure them globally or even on Items. ERPNext taxes are pre-configured for most regions.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
- "modified": "2022-06-07 14:27:15.906286",
+ "modified": "2022-06-14 17:37:56.694261",
"modified_by": "Administrator",
"name": "Setup Taxes",
"owner": "Administrator",
diff --git a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
index 1a00399..230b18c 100644
--- a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
+++ b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
@@ -100,7 +100,7 @@
sales_data = frappe.db.sql(
"""
select s.territory, s.customer, si.item_group, si.item_code, si.qty, {date_field} as last_order_date,
- DATEDIFF(CURDATE(), {date_field}) as days_since_last_order
+ DATEDIFF(CURRENT_DATE, {date_field}) as days_since_last_order
from `tab{doctype}` s, `tab{doctype} Item` si
where s.name = si.parent and s.docstatus = 1
order by days_since_last_order """.format( # nosec
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 2d86dea..ccf4b40 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -2,7 +2,6 @@
# License: GNU General Public License v3. See license.txt
-import itertools
from json import loads
from typing import TYPE_CHECKING, List, Optional, Tuple
@@ -11,7 +10,17 @@
from frappe import _, qb, throw
from frappe.model.meta import get_field_precision
from frappe.query_builder.utils import DocType
-from frappe.utils import cint, cstr, flt, formatdate, get_number_format_info, getdate, now, nowdate
+from frappe.utils import (
+ cint,
+ create_batch,
+ cstr,
+ flt,
+ formatdate,
+ get_number_format_info,
+ getdate,
+ now,
+ nowdate,
+)
from pypika import Order
from pypika.terms import ExistsCriterion
@@ -1149,9 +1158,7 @@
precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit")) or 2
- stock_vouchers_iterator = iter(stock_vouchers)
-
- while stock_vouchers_chunk := list(itertools.islice(stock_vouchers_iterator, GL_REPOSTING_CHUNK)):
+ for stock_vouchers_chunk in create_batch(stock_vouchers, GL_REPOSTING_CHUNK):
gle = get_voucherwise_gl_entries(stock_vouchers_chunk, posting_date)
for voucher_type, voucher_no in stock_vouchers_chunk:
@@ -1168,12 +1175,14 @@
voucher_obj.make_gl_entries(gl_entries=expected_gle, from_repost=True)
else:
_delete_gl_entries(voucher_type, voucher_no)
- frappe.db.commit()
+
+ if not frappe.flags.in_test:
+ frappe.db.commit()
if repost_doc:
repost_doc.db_set(
"gl_reposting_index",
- cint(repost_doc.gl_reposting_index) + GL_REPOSTING_CHUNK,
+ cint(repost_doc.gl_reposting_index) + len(stock_vouchers_chunk),
)
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 1a7f2dd..d732b75 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -330,7 +330,7 @@
else:
# update valid from
frappe.db.sql(
- """UPDATE `tabItem Tax` set valid_from = CURDATE()
+ """UPDATE `tabItem Tax` set valid_from = CURRENT_DATE
where parent = %(item)s and item_tax_template = %(tax)s""",
{"item": item, "tax": tax_template},
)
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 854c0d0..f49366a 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -46,6 +46,7 @@
from erpnext.controllers.sales_and_purchase_return import validate_return
from erpnext.exceptions import InvalidCurrency
from erpnext.setup.utils import get_exchange_rate
+from erpnext.stock.doctype.item.item import get_uom_conv_factor
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
from erpnext.stock.get_item_details import (
_get_item_tax_template,
@@ -548,6 +549,15 @@
if ret.get("pricing_rules"):
self.apply_pricing_rule_on_items(item, ret)
self.set_pricing_rule_details(item, ret)
+ else:
+ # Transactions line item without item code
+
+ uom = item.get("uom")
+ stock_uom = item.get("stock_uom")
+ 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"))
if self.doctype == "Purchase Invoice":
self.set_expense_account(for_validate)
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 1497b18..a725f67 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -691,7 +691,7 @@
def get_batch_numbers(doctype, txt, searchfield, start, page_len, filters):
query = """select batch_id from `tabBatch`
where disabled = 0
- and (expiry_date >= CURDATE() or expiry_date IS NULL)
+ and (expiry_date >= CURRENT_DATE or expiry_date IS NULL)
and name like {txt}""".format(
txt=frappe.db.escape("%{0}%".format(txt))
)
diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py
index b590177..c70a4f6 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.py
+++ b/erpnext/crm/doctype/opportunity/opportunity.py
@@ -8,7 +8,8 @@
from frappe import _
from frappe.email.inbox import link_communication_to_document
from frappe.model.mapper import get_mapped_doc
-from frappe.query_builder import DocType
+from frappe.query_builder import DocType, Interval
+from frappe.query_builder.functions import Now
from frappe.utils import cint, flt, get_fullname
from erpnext.crm.utils import add_link_in_communication, copy_comments
@@ -398,15 +399,17 @@
frappe.db.get_single_value("CRM Settings", "close_opportunity_after_days") or 15
)
- opportunities = frappe.db.sql(
- """ select name from tabOpportunity where status='Replied' and
- modified<DATE_SUB(CURDATE(), INTERVAL %s DAY) """,
- (auto_close_after_days),
- as_dict=True,
- )
+ table = frappe.qb.DocType("Opportunity")
+ opportunities = (
+ frappe.qb.from_(table)
+ .select(table.name)
+ .where(
+ (table.modified < (Now() - Interval(days=auto_close_after_days))) & (table.status == "Replied")
+ )
+ ).run(pluck=True)
for opportunity in opportunities:
- doc = frappe.get_doc("Opportunity", opportunity.get("name"))
+ doc = frappe.get_doc("Opportunity", opportunity)
doc.status = "Closed"
doc.flags.ignore_permissions = True
doc.flags.ignore_mandatory = True
diff --git a/erpnext/hr/doctype/employee_advance/test_employee_advance.py b/erpnext/hr/doctype/employee_advance/test_employee_advance.py
index 44d68c9..81a0876 100644
--- a/erpnext/hr/doctype/employee_advance/test_employee_advance.py
+++ b/erpnext/hr/doctype/employee_advance/test_employee_advance.py
@@ -216,7 +216,7 @@
def make_employee_advance(employee_name, args=None):
doc = frappe.new_doc("Employee Advance")
doc.employee = employee_name
- doc.company = "_Test company"
+ doc.company = "_Test Company"
doc.purpose = "For site visit"
doc.currency = erpnext.get_company_currency("_Test company")
doc.exchange_rate = 1
diff --git a/erpnext/manufacturing/doctype/bom_update_batch/bom_update_batch.json b/erpnext/manufacturing/doctype/bom_update_batch/bom_update_batch.json
index 83b54d3..b867d2a 100644
--- a/erpnext/manufacturing/doctype/bom_update_batch/bom_update_batch.json
+++ b/erpnext/manufacturing/doctype/bom_update_batch/bom_update_batch.json
@@ -1,6 +1,6 @@
{
"actions": [],
- "autoname": "autoincrement",
+ "autoname": "hash",
"creation": "2022-05-31 17:34:39.825537",
"doctype": "DocType",
"engine": "InnoDB",
@@ -46,10 +46,9 @@
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Update Batch",
- "naming_rule": "Autoincrement",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"states": []
-}
\ No newline at end of file
+}
diff --git a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
index 9829a96..549f5af 100644
--- a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
+++ b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
@@ -1,6 +1,6 @@
{
"charts": [],
- "content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order Summary\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
+ "content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Card\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
"creation": "2020-03-02 17:11:37.032604",
"docstatus": 0,
"doctype": "Workspace",
@@ -402,7 +402,7 @@
"type": "Link"
}
],
- "modified": "2022-05-31 22:08:19.408223",
+ "modified": "2022-06-15 15:18:57.062935",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Manufacturing",
@@ -415,39 +415,35 @@
"sequence_id": 17.0,
"shortcuts": [
{
- "color": "Green",
- "format": "{} Active",
- "label": "Item",
- "link_to": "Item",
- "restrict_to_domain": "Manufacturing",
- "stats_filter": "{\n \"disabled\": 0\n}",
- "type": "DocType"
- },
- {
- "color": "Green",
- "format": "{} Active",
+ "color": "Grey",
+ "doc_view": "List",
"label": "BOM",
"link_to": "BOM",
- "restrict_to_domain": "Manufacturing",
- "stats_filter": "{\n \"is_active\": 1\n}",
+ "stats_filter": "{\"is_active\":[\"=\",1]}",
"type": "DocType"
},
{
- "color": "Yellow",
- "format": "{} Open",
- "label": "Work Order",
- "link_to": "Work Order",
- "restrict_to_domain": "Manufacturing",
- "stats_filter": "{ \n \"status\": [\"in\", \n [\"Draft\", \"Not Started\", \"In Process\"]\n ]\n}",
- "type": "DocType"
- },
- {
- "color": "Yellow",
- "format": "{} Open",
+ "color": "Grey",
+ "doc_view": "List",
"label": "Production Plan",
"link_to": "Production Plan",
- "restrict_to_domain": "Manufacturing",
- "stats_filter": "{ \n \"status\": [\"not in\", [\"Completed\"]]\n}",
+ "stats_filter": "{\"status\":[\"not in\",[\"Closed\",\"Cancelled\",\"Completed\"]]}",
+ "type": "DocType"
+ },
+ {
+ "color": "Grey",
+ "doc_view": "List",
+ "label": "Work Order",
+ "link_to": "Work Order",
+ "stats_filter": "{\"status\":[\"not in\",[\"Closed\",\"Cancelled\",\"Completed\"]]}",
+ "type": "DocType"
+ },
+ {
+ "color": "Grey",
+ "doc_view": "List",
+ "label": "Job Card",
+ "link_to": "Job Card",
+ "stats_filter": "{\"status\":[\"not in\",[\"Cancelled\",\"Completed\",null]]}",
"type": "DocType"
},
{
@@ -456,12 +452,6 @@
"type": "Report"
},
{
- "label": "Work Order Summary",
- "link_to": "Work Order Summary",
- "restrict_to_domain": "Manufacturing",
- "type": "Report"
- },
- {
"label": "BOM Stock Report",
"link_to": "BOM Stock Report",
"type": "Report"
@@ -470,12 +460,6 @@
"label": "Production Planning Report",
"link_to": "Production Planning Report",
"type": "Report"
- },
- {
- "label": "Dashboard",
- "link_to": "Manufacturing",
- "restrict_to_domain": "Manufacturing",
- "type": "Dashboard"
}
],
"title": "Manufacturing"
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 5a98463..318875d 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -339,7 +339,7 @@
erpnext.patches.v14_0.delete_healthcare_doctypes
erpnext.patches.v14_0.delete_hub_doctypes
erpnext.patches.v14_0.delete_hospitality_doctypes # 20-01-2022
-erpnext.patches.v14_0.delete_agriculture_doctypes
+erpnext.patches.v14_0.delete_agriculture_doctypes # 15-06-2022
erpnext.patches.v14_0.delete_education_doctypes
erpnext.patches.v14_0.delete_datev_doctypes
erpnext.patches.v14_0.rearrange_company_fields
@@ -374,3 +374,4 @@
execute:frappe.delete_doc("DocType", "Naming Series")
erpnext.patches.v13_0.set_payroll_entry_status
erpnext.patches.v13_0.job_card_status_on_hold
+erpnext.patches.v14_0.migrate_gl_to_payment_ledger
diff --git a/erpnext/patches/v12_0/update_healthcare_refactored_changes.py b/erpnext/patches/v12_0/update_healthcare_refactored_changes.py
deleted file mode 100644
index 5ca0d5d..0000000
--- a/erpnext/patches/v12_0/update_healthcare_refactored_changes.py
+++ /dev/null
@@ -1,131 +0,0 @@
-import frappe
-from frappe.model.utils.rename_field import rename_field
-from frappe.modules import get_doctype_module, scrub
-
-field_rename_map = {
- "Healthcare Settings": [
- ["patient_master_name", "patient_name_by"],
- ["max_visit", "max_visits"],
- ["reg_sms", "send_registration_msg"],
- ["reg_msg", "registration_msg"],
- ["app_con", "send_appointment_confirmation"],
- ["app_con_msg", "appointment_confirmation_msg"],
- ["no_con", "avoid_confirmation"],
- ["app_rem", "send_appointment_reminder"],
- ["app_rem_msg", "appointment_reminder_msg"],
- ["rem_before", "remind_before"],
- ["manage_customer", "link_customer_to_patient"],
- ["create_test_on_si_submit", "create_lab_test_on_si_submit"],
- ["require_sample_collection", "create_sample_collection_for_lab_test"],
- ["require_test_result_approval", "lab_test_approval_required"],
- ["manage_appointment_invoice_automatically", "automate_appointment_invoicing"],
- ],
- "Drug Prescription": [["use_interval", "usage_interval"], ["in_every", "interval_uom"]],
- "Lab Test Template": [
- ["sample_quantity", "sample_qty"],
- ["sample_collection_details", "sample_details"],
- ],
- "Sample Collection": [
- ["sample_quantity", "sample_qty"],
- ["sample_collection_details", "sample_details"],
- ],
- "Fee Validity": [["max_visit", "max_visits"]],
-}
-
-
-def execute():
- for dn in field_rename_map:
- if frappe.db.exists("DocType", dn):
- if dn == "Healthcare Settings":
- frappe.reload_doctype("Healthcare Settings")
- else:
- frappe.reload_doc(get_doctype_module(dn), "doctype", scrub(dn))
-
- for dt, field_list in field_rename_map.items():
- if frappe.db.exists("DocType", dt):
- for field in field_list:
- if dt == "Healthcare Settings":
- rename_field(dt, field[0], field[1])
- elif frappe.db.has_column(dt, field[0]):
- rename_field(dt, field[0], field[1])
-
- # first name mandatory in Patient
- if frappe.db.exists("DocType", "Patient"):
- patients = frappe.db.sql("select name, patient_name from `tabPatient`", as_dict=1)
- frappe.reload_doc("healthcare", "doctype", "patient")
- for entry in patients:
- name = entry.patient_name.split(" ")
- frappe.db.set_value("Patient", entry.name, "first_name", name[0])
-
- # mark Healthcare Practitioner status as Disabled
- if frappe.db.exists("DocType", "Healthcare Practitioner"):
- practitioners = frappe.db.sql(
- "select name from `tabHealthcare Practitioner` where 'active'= 0", as_dict=1
- )
- practitioners_lst = [p.name for p in practitioners]
- frappe.reload_doc("healthcare", "doctype", "healthcare_practitioner")
- if practitioners_lst:
- frappe.db.sql(
- "update `tabHealthcare Practitioner` set status = 'Disabled' where name IN %(practitioners)s"
- "",
- {"practitioners": practitioners_lst},
- )
-
- # set Clinical Procedure status
- if frappe.db.exists("DocType", "Clinical Procedure"):
- frappe.reload_doc("healthcare", "doctype", "clinical_procedure")
- frappe.db.sql(
- """
- UPDATE
- `tabClinical Procedure`
- SET
- docstatus = (CASE WHEN status = 'Cancelled' THEN 2
- WHEN status = 'Draft' THEN 0
- ELSE 1
- END)
- """
- )
-
- # set complaints and diagnosis in table multiselect in Patient Encounter
- if frappe.db.exists("DocType", "Patient Encounter"):
- field_list = [["visit_department", "medical_department"], ["type", "appointment_type"]]
- encounter_details = frappe.db.sql(
- """select symptoms, diagnosis, name from `tabPatient Encounter`""", as_dict=True
- )
- frappe.reload_doc("healthcare", "doctype", "patient_encounter")
- frappe.reload_doc("healthcare", "doctype", "patient_encounter_symptom")
- frappe.reload_doc("healthcare", "doctype", "patient_encounter_diagnosis")
-
- for field in field_list:
- if frappe.db.has_column(dt, field[0]):
- rename_field(dt, field[0], field[1])
-
- for entry in encounter_details:
- doc = frappe.get_doc("Patient Encounter", entry.name)
- symptoms = entry.symptoms.split("\n") if entry.symptoms else []
- for symptom in symptoms:
- if not frappe.db.exists("Complaint", symptom):
- frappe.get_doc({"doctype": "Complaint", "complaints": symptom}).insert()
- row = doc.append("symptoms", {"complaint": symptom})
- row.db_update()
-
- diagnosis = entry.diagnosis.split("\n") if entry.diagnosis else []
- for d in diagnosis:
- if not frappe.db.exists("Diagnosis", d):
- frappe.get_doc({"doctype": "Diagnosis", "diagnosis": d}).insert()
- row = doc.append("diagnosis", {"diagnosis": d})
- row.db_update()
- doc.db_update()
-
- if frappe.db.exists("DocType", "Fee Validity"):
- # update fee validity status
- frappe.db.sql(
- """
- UPDATE
- `tabFee Validity`
- SET
- status = (CASE WHEN visited >= max_visits THEN 'Completed'
- ELSE 'Pending'
- END)
- """
- )
diff --git a/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py b/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py
deleted file mode 100644
index 30b84ac..0000000
--- a/erpnext/patches/v13_0/healthcare_lab_module_rename_doctypes.py
+++ /dev/null
@@ -1,94 +0,0 @@
-import frappe
-from frappe.model.utils.rename_field import rename_field
-
-
-def execute():
- if frappe.db.exists("DocType", "Lab Test") and frappe.db.exists("DocType", "Lab Test Template"):
- # rename child doctypes
- doctypes = {
- "Lab Test Groups": "Lab Test Group Template",
- "Normal Test Items": "Normal Test Result",
- "Sensitivity Test Items": "Sensitivity Test Result",
- "Special Test Items": "Descriptive Test Result",
- "Special Test Template": "Descriptive Test Template",
- }
-
- frappe.reload_doc("healthcare", "doctype", "lab_test")
- frappe.reload_doc("healthcare", "doctype", "lab_test_template")
-
- for old_dt, new_dt in doctypes.items():
- frappe.flags.link_fields = {}
- should_rename = frappe.db.table_exists(old_dt) and not frappe.db.table_exists(new_dt)
- if should_rename:
- frappe.reload_doc("healthcare", "doctype", frappe.scrub(old_dt))
- frappe.rename_doc("DocType", old_dt, new_dt, force=True)
- frappe.reload_doc("healthcare", "doctype", frappe.scrub(new_dt))
- frappe.delete_doc_if_exists("DocType", old_dt)
-
- parent_fields = {
- "Lab Test Group Template": "lab_test_groups",
- "Descriptive Test Template": "descriptive_test_templates",
- "Normal Test Result": "normal_test_items",
- "Sensitivity Test Result": "sensitivity_test_items",
- "Descriptive Test Result": "descriptive_test_items",
- }
-
- for doctype, parentfield in parent_fields.items():
- frappe.db.sql(
- """
- UPDATE `tab{0}`
- SET parentfield = %(parentfield)s
- """.format(
- doctype
- ),
- {"parentfield": parentfield},
- )
-
- # copy renamed child table fields (fields were already renamed in old doctype json, hence sql)
- rename_fields = {
- "lab_test_name": "test_name",
- "lab_test_event": "test_event",
- "lab_test_uom": "test_uom",
- "lab_test_comment": "test_comment",
- }
-
- for new, old in rename_fields.items():
- if frappe.db.has_column("Normal Test Result", old):
- frappe.db.sql("""UPDATE `tabNormal Test Result` SET {} = {}""".format(new, old))
-
- if frappe.db.has_column("Normal Test Template", "test_event"):
- frappe.db.sql("""UPDATE `tabNormal Test Template` SET lab_test_event = test_event""")
-
- if frappe.db.has_column("Normal Test Template", "test_uom"):
- frappe.db.sql("""UPDATE `tabNormal Test Template` SET lab_test_uom = test_uom""")
-
- if frappe.db.has_column("Descriptive Test Result", "test_particulars"):
- frappe.db.sql(
- """UPDATE `tabDescriptive Test Result` SET lab_test_particulars = test_particulars"""
- )
-
- rename_fields = {
- "lab_test_template": "test_template",
- "lab_test_description": "test_description",
- "lab_test_rate": "test_rate",
- }
-
- for new, old in rename_fields.items():
- if frappe.db.has_column("Lab Test Group Template", old):
- frappe.db.sql("""UPDATE `tabLab Test Group Template` SET {} = {}""".format(new, old))
-
- # rename field
- frappe.reload_doc("healthcare", "doctype", "lab_test")
- if frappe.db.has_column("Lab Test", "special_toggle"):
- rename_field("Lab Test", "special_toggle", "descriptive_toggle")
-
- if frappe.db.exists("DocType", "Lab Test Group Template"):
- # fix select field option
- frappe.reload_doc("healthcare", "doctype", "lab_test_group_template")
- frappe.db.sql(
- """
- UPDATE `tabLab Test Group Template`
- SET template_or_new_line = 'Add New Line'
- WHERE template_or_new_line = 'Add new line'
- """
- )
diff --git a/erpnext/patches/v13_0/print_uom_after_quantity_patch.py b/erpnext/patches/v13_0/print_uom_after_quantity_patch.py
deleted file mode 100644
index a16f909..0000000
--- a/erpnext/patches/v13_0/print_uom_after_quantity_patch.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2019, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-from erpnext.setup.install import create_print_uom_after_qty_custom_field
-
-
-def execute():
- create_print_uom_after_qty_custom_field()
diff --git a/erpnext/patches/v13_0/rename_discharge_date_in_ip_record.py b/erpnext/patches/v13_0/rename_discharge_date_in_ip_record.py
deleted file mode 100644
index 3bd717d..0000000
--- a/erpnext/patches/v13_0/rename_discharge_date_in_ip_record.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import frappe
-from frappe.model.utils.rename_field import rename_field
-
-
-def execute():
- frappe.reload_doc("Healthcare", "doctype", "Inpatient Record")
- if frappe.db.has_column("Inpatient Record", "discharge_date"):
- rename_field("Inpatient Record", "discharge_date", "discharge_datetime")
diff --git a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py
deleted file mode 100644
index bc2d1b9..0000000
--- a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import frappe
-
-
-def execute():
- company = frappe.db.get_single_value("Global Defaults", "default_company")
- doctypes = [
- "Clinical Procedure",
- "Inpatient Record",
- "Lab Test",
- "Sample Collection",
- "Patient Appointment",
- "Patient Encounter",
- "Vital Signs",
- "Therapy Session",
- "Therapy Plan",
- "Patient Assessment",
- ]
- for entry in doctypes:
- if frappe.db.exists("DocType", entry):
- frappe.reload_doc("Healthcare", "doctype", entry)
- frappe.db.sql(
- "update `tab{dt}` set company = {company} where ifnull(company, '') = ''".format(
- dt=entry, company=frappe.db.escape(company)
- )
- )
diff --git a/erpnext/patches/v14_0/delete_agriculture_doctypes.py b/erpnext/patches/v14_0/delete_agriculture_doctypes.py
index e0b12a2..8ec0c33 100644
--- a/erpnext/patches/v14_0/delete_agriculture_doctypes.py
+++ b/erpnext/patches/v14_0/delete_agriculture_doctypes.py
@@ -2,6 +2,9 @@
def execute():
+ if "agriculture" in frappe.get_installed_apps():
+ return
+
frappe.delete_doc("Module Def", "Agriculture", ignore_missing=True, force=True)
frappe.delete_doc("Workspace", "Agriculture", ignore_missing=True, force=True)
@@ -19,3 +22,5 @@
doctypes = frappe.get_all("DocType", {"module": "agriculture", "custom": 0}, pluck="name")
for doctype in doctypes:
frappe.delete_doc("DocType", doctype, ignore_missing=True)
+
+ frappe.delete_doc("Module Def", "Agriculture", ignore_missing=True, force=True)
diff --git a/erpnext/projects/doctype/project_update/project_update.py b/erpnext/projects/doctype/project_update/project_update.py
index 5a29fb6..175f787 100644
--- a/erpnext/projects/doctype/project_update/project_update.py
+++ b/erpnext/projects/doctype/project_update/project_update.py
@@ -28,7 +28,7 @@
for drafts in draft:
number_of_drafts = drafts[0]
update = frappe.db.sql(
- """SELECT name,date,time,progress,progress_details FROM `tabProject Update` WHERE `tabProject Update`.project = %s AND date = DATE_ADD(CURDATE(), INTERVAL -1 DAY);""",
+ """SELECT name,date,time,progress,progress_details FROM `tabProject Update` WHERE `tabProject Update`.project = %s AND date = DATE_ADD(CURRENT_DATE, INTERVAL -1 DAY);""",
project_name,
)
email_sending(project_name, frequency, date_start, date_end, progress, number_of_drafts, update)
@@ -39,7 +39,7 @@
):
holiday = frappe.db.sql(
- """SELECT holiday_date FROM `tabHoliday` where holiday_date = CURDATE();"""
+ """SELECT holiday_date FROM `tabHoliday` where holiday_date = CURRENT_DATE;"""
)
msg = (
"<p>Project Name: "
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index de93c82..01f72ad 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -453,7 +453,6 @@
is_pos: cint(me.frm.doc.is_pos),
is_return: cint(me.frm.doc.is_return),
is_subcontracted: me.frm.doc.is_subcontracted,
- transaction_date: me.frm.doc.transaction_date || me.frm.doc.posting_date,
ignore_pricing_rule: me.frm.doc.ignore_pricing_rule,
doctype: me.frm.doc.doctype,
name: me.frm.doc.name,
diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json
index 5dfd8f2..bb2f95d 100644
--- a/erpnext/selling/doctype/quotation/quotation.json
+++ b/erpnext/selling/doctype/quotation/quotation.json
@@ -296,7 +296,7 @@
"read_only": 1
},
{
- "depends_on": "eval:doc.quotaion_to=='Customer' && doc.party_name",
+ "depends_on": "eval:doc.quotation_to=='Customer' && doc.party_name",
"fieldname": "col_break98",
"fieldtype": "Column Break",
"width": "50%"
@@ -316,7 +316,7 @@
"read_only": 1
},
{
- "depends_on": "eval:doc.quotaion_to=='Customer' && doc.party_name",
+ "depends_on": "eval:doc.quotation_to=='Customer' && doc.party_name",
"fieldname": "customer_group",
"fieldtype": "Link",
"hidden": 1,
@@ -1084,4 +1084,4 @@
"states": [],
"timeline_field": "party_name",
"title_field": "title"
-}
\ No newline at end of file
+}
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 7522e92..8c03cb5 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -25,6 +25,7 @@
from erpnext.selling.doctype.customer.customer import check_credit_limit
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
from erpnext.stock.doctype.item.item import get_item_defaults
+from erpnext.stock.get_item_details import get_default_bom
from erpnext.stock.stock_balance import get_reserved_qty, update_bin_qty
form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
@@ -423,8 +424,9 @@
for table in [self.items, self.packed_items]:
for i in table:
- bom = get_default_bom_item(i.item_code)
+ bom = get_default_bom(i.item_code)
stock_qty = i.qty if i.doctype == "Packed Item" else i.stock_qty
+
if not for_raw_material_request:
total_work_order_qty = flt(
frappe.db.sql(
@@ -438,32 +440,19 @@
pending_qty = stock_qty
if pending_qty and i.item_code not in product_bundle_parents:
- if bom:
- items.append(
- dict(
- name=i.name,
- item_code=i.item_code,
- description=i.description,
- bom=bom,
- warehouse=i.warehouse,
- pending_qty=pending_qty,
- required_qty=pending_qty if for_raw_material_request else 0,
- sales_order_item=i.name,
- )
+ items.append(
+ dict(
+ name=i.name,
+ item_code=i.item_code,
+ description=i.description,
+ bom=bom or "",
+ warehouse=i.warehouse,
+ pending_qty=pending_qty,
+ required_qty=pending_qty if for_raw_material_request else 0,
+ sales_order_item=i.name,
)
- else:
- items.append(
- dict(
- name=i.name,
- item_code=i.item_code,
- description=i.description,
- bom="",
- warehouse=i.warehouse,
- pending_qty=pending_qty,
- required_qty=pending_qty if for_raw_material_request else 0,
- sales_order_item=i.name,
- )
- )
+ )
+
return items
def on_recurring(self, reference_doc, auto_repeat_doc):
@@ -1167,13 +1156,6 @@
so.update_status(status)
-def get_default_bom_item(item_code):
- bom = frappe.get_all("BOM", dict(item=item_code, is_active=True), order_by="is_default desc")
- bom = bom[0].name if bom else None
-
- return bom
-
-
@frappe.whitelist()
def make_raw_material_request(items, company, sales_order, project=None):
if not frappe.has_permission("Sales Order", "write"):
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 96308f0..45868fb 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -644,7 +644,7 @@
else:
# update valid from
frappe.db.sql(
- """UPDATE `tabItem Tax` set valid_from = CURDATE()
+ """UPDATE `tabItem Tax` set valid_from = CURRENT_DATE
where parent = %(item)s and item_tax_template = %(tax)s""",
{"item": item, "tax": tax_template},
)
@@ -1380,6 +1380,59 @@
except Exception:
self.fail("Can not cancel sales order with linked cancelled payment entry")
+ def test_work_order_pop_up_from_sales_order(self):
+ "Test `get_work_order_items` in Sales Order picks the right BOM for items to manufacture."
+
+ from erpnext.controllers.item_variant import create_variant
+ from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+
+ make_item( # template item
+ "Test-WO-Tshirt",
+ {
+ "has_variant": 1,
+ "variant_based_on": "Item Attribute",
+ "attributes": [{"attribute": "Test Colour"}],
+ },
+ )
+ make_item("Test-RM-Cotton") # RM for BOM
+
+ for colour in (
+ "Red",
+ "Green",
+ ):
+ variant = create_variant("Test-WO-Tshirt", {"Test Colour": colour})
+ variant.save()
+
+ template_bom = make_bom(item="Test-WO-Tshirt", rate=100, raw_materials=["Test-RM-Cotton"])
+ red_var_bom = make_bom(item="Test-WO-Tshirt-R", rate=100, raw_materials=["Test-RM-Cotton"])
+
+ so = make_sales_order(
+ **{
+ "item_list": [
+ {
+ "item_code": "Test-WO-Tshirt-R",
+ "qty": 1,
+ "rate": 1000,
+ "warehouse": "_Test Warehouse - _TC",
+ },
+ {
+ "item_code": "Test-WO-Tshirt-G",
+ "qty": 1,
+ "rate": 1000,
+ "warehouse": "_Test Warehouse - _TC",
+ },
+ ]
+ }
+ )
+ wo_items = so.get_work_order_items()
+
+ self.assertEqual(wo_items[0].get("item_code"), "Test-WO-Tshirt-R")
+ self.assertEqual(wo_items[0].get("bom"), red_var_bom.name)
+
+ # Must pick Template Item BOM for Test-WO-Tshirt-G as it has no BOM
+ self.assertEqual(wo_items[1].get("item_code"), "Test-WO-Tshirt-G")
+ self.assertEqual(wo_items[1].get("bom"), template_bom.name)
+
def test_request_for_raw_materials(self):
item = make_item(
"_Test Finished Item",
diff --git a/erpnext/selling/report/inactive_customers/inactive_customers.py b/erpnext/selling/report/inactive_customers/inactive_customers.py
index 1b337fc..a166085 100644
--- a/erpnext/selling/report/inactive_customers/inactive_customers.py
+++ b/erpnext/selling/report/inactive_customers/inactive_customers.py
@@ -31,13 +31,13 @@
def get_sales_details(doctype):
cond = """sum(so.base_net_total) as 'total_order_considered',
max(so.posting_date) as 'last_order_date',
- DATEDIFF(CURDATE(), max(so.posting_date)) as 'days_since_last_order' """
+ DATEDIFF(CURRENT_DATE, max(so.posting_date)) as 'days_since_last_order' """
if doctype == "Sales Order":
cond = """sum(if(so.status = "Stopped",
so.base_net_total * so.per_delivered/100,
so.base_net_total)) as 'total_order_considered',
max(so.transaction_date) as 'last_order_date',
- DATEDIFF(CURDATE(), max(so.transaction_date)) as 'days_since_last_order'"""
+ DATEDIFF(CURRENT_DATE, max(so.transaction_date)) as 'days_since_last_order'"""
return frappe.db.sql(
"""select
diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
index cc61594..720aa41 100644
--- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
+++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
@@ -64,7 +64,7 @@
soi.delivery_date as delivery_date,
so.name as sales_order,
so.status, so.customer, soi.item_code,
- DATEDIFF(CURDATE(), soi.delivery_date) as delay_days,
+ DATEDIFF(CURRENT_DATE, soi.delivery_date) as delay_days,
IF(so.status in ('Completed','To Bill'), 0, (SELECT delay_days)) as delay,
soi.qty, soi.delivered_qty,
(soi.qty - soi.delivered_qty) AS pending_qty,
diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py
index cdfea77..42ba6ce 100644
--- a/erpnext/setup/doctype/email_digest/email_digest.py
+++ b/erpnext/setup/doctype/email_digest/email_digest.py
@@ -854,7 +854,7 @@
sql_po = """select {fields} from `tabPurchase Order Item`
left join `tabPurchase Order` on `tabPurchase Order`.name = `tabPurchase Order Item`.parent
- where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and curdate() > `tabPurchase Order Item`.schedule_date
+ where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and CURRENT_DATE > `tabPurchase Order Item`.schedule_date
and received_qty < qty order by `tabPurchase Order Item`.parent DESC,
`tabPurchase Order Item`.schedule_date DESC""".format(
fields=fields_po
@@ -862,7 +862,7 @@
sql_poi = """select {fields} from `tabPurchase Order Item`
left join `tabPurchase Order` on `tabPurchase Order`.name = `tabPurchase Order Item`.parent
- where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and curdate() > `tabPurchase Order Item`.schedule_date
+ where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and CURRENT_DATE > `tabPurchase Order Item`.schedule_date
and received_qty < qty order by `tabPurchase Order Item`.idx""".format(
fields=fields_poi
)
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index 559883f..52854a0 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -335,7 +335,7 @@
on (`tabBatch`.batch_id = `tabStock Ledger Entry`.batch_no )
where `tabStock Ledger Entry`.item_code = %s and `tabStock Ledger Entry`.warehouse = %s
and `tabStock Ledger Entry`.is_cancelled = 0
- and (`tabBatch`.expiry_date >= CURDATE() or `tabBatch`.expiry_date IS NULL) {0}
+ and (`tabBatch`.expiry_date >= CURRENT_DATE or `tabBatch`.expiry_date IS NULL) {0}
group by batch_id
order by `tabBatch`.expiry_date ASC, `tabBatch`.creation ASC
""".format(
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index 2f6d4fb..76cb31d 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -14,7 +14,6 @@
"details",
"naming_series",
"item_code",
- "variant_of",
"item_name",
"item_group",
"stock_uom",
@@ -22,6 +21,7 @@
"disabled",
"allow_alternative_item",
"is_stock_item",
+ "has_variants",
"include_item_in_manufacturing",
"opening_stock",
"valuation_rate",
@@ -66,7 +66,7 @@
"has_serial_no",
"serial_no_series",
"variants_section",
- "has_variants",
+ "variant_of",
"variant_based_on",
"attributes",
"accounting",
@@ -112,8 +112,8 @@
"quality_inspection_template",
"inspection_required_before_delivery",
"manufacturing",
- "default_bom",
"is_sub_contracted_item",
+ "default_bom",
"column_break_74",
"customer_code",
"default_item_manufacturer",
@@ -479,7 +479,7 @@
"collapsible_depends_on": "attributes",
"depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "variants_section",
- "fieldtype": "Section Break",
+ "fieldtype": "Tab Break",
"label": "Variants"
},
{
@@ -504,7 +504,8 @@
"fieldname": "attributes",
"fieldtype": "Table",
"hidden": 1,
- "label": "Attributes",
+ "label": "Variant Attributes",
+ "mandatory_depends_on": "has_variants",
"options": "Item Variant Attribute"
},
{
@@ -909,7 +910,7 @@
"image_field": "image",
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2022-06-08 11:35:20.094546",
+ "modified": "2022-06-15 09:02:06.177691",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",
diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
index 1af9953..1ba8011 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
@@ -24,7 +24,7 @@
pr = make_purchase_receipt(
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
- supplier_warehouse="Work in Progress - TCP1",
+ supplier_warehouse="Work In Progress - TCP1",
get_multiple_items=True,
get_taxes_and_charges=True,
)
@@ -195,7 +195,7 @@
pr = make_purchase_receipt(
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
- supplier_warehouse="Work in Progress - TCP1",
+ supplier_warehouse="Work In Progress - TCP1",
get_multiple_items=True,
get_taxes_and_charges=True,
do_not_submit=True,
@@ -280,7 +280,7 @@
pr = make_purchase_receipt(
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
- supplier_warehouse="Work in Progress - TCP1",
+ supplier_warehouse="Work In Progress - TCP1",
do_not_save=True,
)
pr.items[0].cost_center = "Main - TCP1"
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 7fbfa62..be4f274 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -276,7 +276,7 @@
pr = make_purchase_receipt(
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
- supplier_warehouse="Work in Progress - TCP1",
+ supplier_warehouse="Work In Progress - TCP1",
get_multiple_items=True,
get_taxes_and_charges=True,
)
@@ -486,13 +486,13 @@
pr = make_purchase_receipt(
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
- supplier_warehouse="Work in Progress - 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",
+ supplier_warehouse="Work In Progress - TCP1",
is_return=1,
return_against=pr.name,
qty=-2,
@@ -573,13 +573,13 @@
pr = make_purchase_receipt(
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
- supplier_warehouse="Work in Progress - 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",
+ supplier_warehouse="Work In Progress - TCP1",
is_return=1,
return_against=pr.name,
qty=-5,
@@ -615,7 +615,7 @@
pr = make_purchase_receipt(
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
- supplier_warehouse="Work in Progress - TCP1",
+ supplier_warehouse="Work In Progress - TCP1",
qty=2,
rejected_qty=2,
rejected_warehouse=rejected_warehouse,
@@ -624,7 +624,7 @@
return_pr = make_purchase_receipt(
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
- supplier_warehouse="Work in Progress - TCP1",
+ supplier_warehouse="Work In Progress - TCP1",
is_return=1,
return_against=pr.name,
qty=-2,
@@ -951,7 +951,7 @@
cost_center=cost_center,
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
- supplier_warehouse="Work in Progress - TCP1",
+ supplier_warehouse="Work In Progress - TCP1",
)
stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse)
@@ -975,7 +975,7 @@
pr = make_purchase_receipt(
company="_Test Company with perpetual inventory",
warehouse="Stores - TCP1",
- supplier_warehouse="Work in Progress - TCP1",
+ supplier_warehouse="Work In Progress - TCP1",
)
stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse)
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index ea24b47..b1017d2 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -87,6 +87,7 @@
self.current_index = 0
self.distinct_item_and_warehouse = None
self.items_to_be_repost = None
+ self.gl_reposting_index = 0
self.db_update()
def deduplicate_similar_repost(self):
diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
index 3c74619..edd2553 100644
--- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
@@ -7,7 +7,7 @@
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import nowdate
-from frappe.utils.data import today
+from frappe.utils.data import add_to_date, today
from erpnext.accounts.utils import repost_gle_for_stock_vouchers
from erpnext.controllers.stock_controller import create_item_wise_repost_entries
@@ -17,10 +17,11 @@
in_configured_timeslot,
)
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext.stock.tests.test_utils import StockTestMixin
from erpnext.stock.utils import PendingRepostingError
-class TestRepostItemValuation(FrappeTestCase):
+class TestRepostItemValuation(FrappeTestCase, StockTestMixin):
def tearDown(self):
frappe.flags.dont_execute_stock_reposts = False
@@ -225,3 +226,49 @@
repost_gle_for_stock_vouchers(stock_vouchers=vouchers, posting_date=posting_date, repost_doc=doc)
self.assertNotIn(call("gl_reposting_index", 1), doc.db_set.mock_calls)
+
+ def test_gl_complete_gl_reposting(self):
+ from erpnext.accounts import utils
+
+ # lower numbers to simplify test
+ orig_chunk_size = utils.GL_REPOSTING_CHUNK
+ utils.GL_REPOSTING_CHUNK = 2
+ self.addCleanup(setattr, utils, "GL_REPOSTING_CHUNK", orig_chunk_size)
+
+ item = self.make_item().name
+
+ company = "_Test Company with perpetual inventory"
+
+ for _ in range(10):
+ make_stock_entry(item=item, company=company, qty=1, rate=10, target="Stores - TCP1")
+
+ # consume
+ consumption = make_stock_entry(item=item, company=company, qty=1, source="Stores - TCP1")
+
+ self.assertGLEs(
+ consumption,
+ [{"credit": 10, "debit": 0}],
+ gle_filters={"account": "Stock In Hand - TCP1"},
+ )
+
+ # backdated receipt
+ backdated_receipt = make_stock_entry(
+ item=item,
+ company=company,
+ qty=1,
+ rate=50,
+ target="Stores - TCP1",
+ posting_date=add_to_date(today(), days=-1),
+ )
+ self.assertGLEs(
+ backdated_receipt,
+ [{"credit": 0, "debit": 50}],
+ gle_filters={"account": "Stock In Hand - TCP1"},
+ )
+
+ # check that original consumption GLe is updated
+ self.assertGLEs(
+ consumption,
+ [{"credit": 50, "debit": 0}],
+ gle_filters={"account": "Stock In Hand - TCP1"},
+ )
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index c8d9f54..7cff85f 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -63,18 +63,16 @@
item = frappe.get_cached_doc("Item", args.item_code)
validate_item_details(args, item)
- out = get_basic_details(args, item, overwrite_warehouse)
-
if isinstance(doc, str):
doc = json.loads(doc)
- if doc and doc.get("doctype") == "Purchase Invoice":
- args["bill_date"] = doc.get("bill_date")
-
if doc:
- args["posting_date"] = doc.get("posting_date")
- args["transaction_date"] = doc.get("transaction_date")
+ args["transaction_date"] = doc.get("transaction_date") or doc.get("posting_date")
+ if doc.get("doctype") == "Purchase Invoice":
+ args["bill_date"] = doc.get("bill_date")
+
+ out = get_basic_details(args, item, overwrite_warehouse)
get_item_tax_template(args, item, out)
out["item_tax_rate"] = get_item_tax_map(
args.company,
@@ -596,9 +594,7 @@
if tax.valid_from or tax.maximum_net_rate:
# In purchase Invoice first preference will be given to supplier invoice date
# if supplier date is not present then posting date
- validation_date = (
- args.get("transaction_date") or args.get("bill_date") or args.get("posting_date")
- )
+ validation_date = args.get("bill_date") or args.get("transaction_date")
if getdate(tax.valid_from) <= getdate(validation_date) and is_within_valid_range(args, tax):
taxes_with_validity.append(tax)
@@ -891,10 +887,6 @@
conditions += """ and %(transaction_date)s between
ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')"""
- if args.get("posting_date"):
- conditions += """ and %(posting_date)s between
- ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')"""
-
return frappe.db.sql(
""" select name, price_list_rate, uom
from `tabItem Price` {conditions}
@@ -921,7 +913,6 @@
"supplier": args.get("supplier"),
"uom": args.get("uom"),
"transaction_date": args.get("transaction_date"),
- "posting_date": args.get("posting_date"),
"batch_no": args.get("batch_no"),
}
@@ -1352,12 +1343,22 @@
@frappe.whitelist()
def get_default_bom(item_code=None):
- if item_code:
- bom = frappe.db.get_value(
- "BOM", {"docstatus": 1, "is_default": 1, "is_active": 1, "item": item_code}
+ def _get_bom(item):
+ bom = frappe.get_all(
+ "BOM", dict(item=item, is_active=True, is_default=True, docstatus=1), limit=1
)
- if bom:
- return bom
+ return bom[0].name if bom else None
+
+ if not item_code:
+ return
+
+ bom_name = _get_bom(item_code)
+
+ template_item = frappe.db.get_value("Item", item_code, "variant_of")
+ if not bom_name and template_item:
+ bom_name = _get_bom(template_item)
+
+ return bom_name
@frappe.whitelist()
diff --git a/erpnext/stock/tests/test_utils.py b/erpnext/stock/tests/test_utils.py
index b046dbd..4e93ac9 100644
--- a/erpnext/stock/tests/test_utils.py
+++ b/erpnext/stock/tests/test_utils.py
@@ -26,6 +26,7 @@
filters=filters,
order_by="timestamp(posting_date, posting_time), creation",
)
+ self.assertGreaterEqual(len(sles), len(expected_sles))
for exp_sle, act_sle in zip(expected_sles, sles):
for k, v in exp_sle.items():
@@ -49,7 +50,7 @@
filters=filters,
order_by=order_by or "posting_date, creation",
)
-
+ self.assertGreaterEqual(len(actual_gles), len(expected_gles))
for exp_gle, act_gle in zip(expected_gles, actual_gles):
for k, exp_value in exp_gle.items():
act_value = act_gle[k]
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index 08a06b1..7f3e0cf 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -11,6 +11,8 @@
from frappe.email.inbox import link_communication_to_document
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
+from frappe.query_builder import Interval
+from frappe.query_builder.functions import Now
from frappe.utils import date_diff, get_datetime, now_datetime, time_diff_in_seconds
from frappe.utils.user import is_website_user
@@ -190,15 +192,17 @@
frappe.db.get_value("Support Settings", "Support Settings", "close_issue_after_days") or 7
)
- issues = frappe.db.sql(
- """ select name from tabIssue where status='Replied' and
- modified<DATE_SUB(CURDATE(), INTERVAL %s DAY) """,
- (auto_close_after_days),
- as_dict=True,
- )
+ table = frappe.qb.DocType("Issue")
+ issues = (
+ frappe.qb.from_(table)
+ .select(table.name)
+ .where(
+ (table.modified < (Now() - Interval(days=auto_close_after_days))) & (table.status == "Replied")
+ )
+ ).run(pluck=True)
for issue in issues:
- doc = frappe.get_doc("Issue", issue.get("name"))
+ doc = frappe.get_doc("Issue", issue)
doc.status = "Closed"
doc.flags.ignore_permissions = True
doc.flags.ignore_mandatory = True
diff --git a/erpnext/tests/test_init.py b/erpnext/tests/test_init.py
index 4d5fced..18ce93a 100644
--- a/erpnext/tests/test_init.py
+++ b/erpnext/tests/test_init.py
@@ -45,3 +45,8 @@
from frappe.tests.test_translate import verify_translation_files
verify_translation_files("erpnext")
+
+ def test_patches(self):
+ from frappe.tests.test_patches import check_patch_files
+
+ check_patch_files("erpnext")
diff --git a/sponsors.md b/sponsors.md
index 125b358..57adc8d 100644
--- a/sponsors.md
+++ b/sponsors.md
@@ -61,5 +61,13 @@
Bulk edit via export-import in Bank Reconciliation <a href="https://github.com/frappe/erpnext/issues/1938">#4356</a>
</td>
</tr>
+ <tr>
+ <td style="width: 30%">
+ <a href="https://www.sapconinstruments.com/">Sapcon Instruments Pvt Ltd</a>
+ </td>
+ <td>
+ Level wise BOM Cost Updation and Performance Enhancement <a href="https://github.com/frappe/erpnext/pull/31072">#31072</a>
+ </td>
+ </tr>
</tbody>
</table>