diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index a10a810d1de..f7a57bb96e8 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1444,7 +1444,7 @@ def get_negative_outstanding_invoices( 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 2438f4b1ab8..98e0a9b2158 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -36,8 +36,12 @@ class PricingRule(Document): 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 3c70e24cae1..6412da709f1 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 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin): 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 b9040e33097..9916d1622d1 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 b6e9f5cd878..e323f6cb1a9 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 1a003993aac..230b18c293f 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 @@ def get_sales_details(filters): 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 2d86dead2fd..ccf4b402465 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 @@ import frappe.defaults 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 @@ def repost_gle_for_stock_vouchers( 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 @@ def repost_gle_for_stock_vouchers( 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 1a7f2dd5d97..d732b755fef 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 @@ class TestPurchaseOrder(FrappeTestCase): 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 854c0d00f54..f49366a9562 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -46,6 +46,7 @@ from erpnext.controllers.print_settings import ( 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 @@ class AccountsController(TransactionBase): 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 1497b182e59..a725f674c97 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -691,7 +691,7 @@ def get_doctype_wise_filters(filters): 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 b590177562d..c70a4f61b8f 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -8,7 +8,8 @@ import frappe 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 @@ def auto_close_opportunity(): 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 - modifiedYour Shortcuts\",\"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\":\"Reports & Masters\",\"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\":\"Your Shortcuts\",\"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\":\"Reports & Masters\",\"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" }, { @@ -455,12 +451,6 @@ "link_to": "Exponential Smoothing Forecasting", "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", @@ -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 5a984635fdc..318875d2a43 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -339,7 +339,7 @@ erpnext.patches.v14_0.delete_shopify_doctypes 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 @@ erpnext.patches.v13_0.set_per_billed_in_return_delivery_note 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 5ca0d5d47d9..00000000000 --- 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 30b84accf3f..00000000000 --- 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 a16f909fc38..00000000000 --- 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 3bd717d77b8..00000000000 --- 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 bc2d1b94f79..00000000000 --- 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 e0b12a25799..8ec0c33090d 100644 --- a/erpnext/patches/v14_0/delete_agriculture_doctypes.py +++ b/erpnext/patches/v14_0/delete_agriculture_doctypes.py @@ -2,6 +2,9 @@ import frappe 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 @@ def execute(): 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 5a29fb6c33e..175f787a303 100644 --- a/erpnext/projects/doctype/project_update/project_update.py +++ b/erpnext/projects/doctype/project_update/project_update.py @@ -28,7 +28,7 @@ def daily_reminder(): 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 @@ def email_sending( ): holiday = frappe.db.sql( - """SELECT holiday_date FROM `tabHoliday` where holiday_date = CURDATE();""" + """SELECT holiday_date FROM `tabHoliday` where holiday_date = CURRENT_DATE;""" ) msg = ( "

Project Name: " diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index de93c82ef2c..01f72adf349 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -453,7 +453,6 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe 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 5dfd8f29651..bb2f95dd173 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 7522e92a8ae..8c03cb5b415 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.manufacturing.doctype.production_plan.production_plan import ( 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 @@ class SalesOrder(SellingController): 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 @@ class SalesOrder(SellingController): 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, - ) - ) - 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, - ) + 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, ) + ) + return items def on_recurring(self, reference_doc, auto_repeat_doc): @@ -1167,13 +1156,6 @@ def update_status(status, name): 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 96308f0bee6..45868fb3293 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 @@ class TestSalesOrder(FrappeTestCase): 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 @@ class TestSalesOrder(FrappeTestCase): 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 1b337fc495e..a1660853272 100644 --- a/erpnext/selling/report/inactive_customers/inactive_customers.py +++ b/erpnext/selling/report/inactive_customers/inactive_customers.py @@ -31,13 +31,13 @@ def execute(filters=None): 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 cc61594af4a..720aa41982a 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 @@ def get_data(conditions, filters): 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 cdfea7764f1..42ba6ce3944 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -854,7 +854,7 @@ class EmailDigest(Document): 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 @@ class EmailDigest(Document): 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 559883f2240..52854a0f013 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -335,7 +335,7 @@ def get_batches(item_code, warehouse, qty=1, throw=False, serial_no=None): 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 2f6d4fb783d..76cb31dc42e 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 1af99534516..1ba801134e2 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 @@ class TestLandedCostVoucher(FrappeTestCase): 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 @@ class TestLandedCostVoucher(FrappeTestCase): 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 @@ class TestLandedCostVoucher(FrappeTestCase): 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 7fbfa629392..be4f27465ed 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 @@ class TestPurchaseReceipt(FrappeTestCase): 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 @@ class TestPurchaseReceipt(FrappeTestCase): 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 @@ class TestPurchaseReceipt(FrappeTestCase): 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 @@ class TestPurchaseReceipt(FrappeTestCase): 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 @@ class TestPurchaseReceipt(FrappeTestCase): 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 @@ class TestPurchaseReceipt(FrappeTestCase): 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 @@ class TestPurchaseReceipt(FrappeTestCase): 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 ea24b47a295..b1017d2c9c0 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 @@ class RepostItemValuation(Document): 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 3c74619b461..edd2553d5d1 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 @@ from unittest.mock import MagicMock, call 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 @@ from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import ( 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 @@ class TestRepostItemValuation(FrappeTestCase): 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 c8d9f5404fb..7cff85fb571 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -63,18 +63,16 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru 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 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False): 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 @@ def get_item_price(args, item_code, ignore_party=False): 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 @@ def get_price_list_rate_for(args, item_code): "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 @@ def get_price_list_currency_and_exchange_rate(args): @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 b046dbda24f..4e93ac93cb7 100644 --- a/erpnext/stock/tests/test_utils.py +++ b/erpnext/stock/tests/test_utils.py @@ -26,6 +26,7 @@ class StockTestMixin: 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 @@ class StockTestMixin: 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 08a06b19b43..7f3e0cf4c21 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -11,6 +11,8 @@ from frappe.core.utils import get_parent_doc 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 @@ def auto_close_tickets(): 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#4356 + + + Sapcon Instruments Pvt Ltd + + + Level wise BOM Cost Updation and Performance Enhancement #31072 + +