Merge branch 'develop' into fixed-transferred-materials-are-not-consumed

This commit is contained in:
rohitwaghchaure 2022-06-17 15:02:07 +05:30 committed by GitHub
commit 2ec133df6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 300 additions and 430 deletions

View File

@ -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"

View File

@ -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))

View File

@ -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(

View File

@ -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",

View File

@ -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",

View File

@ -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

View File

@ -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),
)

View File

@ -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},
)

View File

@ -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)

View File

@ -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))
)

View File

@ -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
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

View File

@ -216,7 +216,7 @@ def make_payment_entry(advance):
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

View File

@ -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": []
}
}

View File

@ -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 &amp; 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"
},
{
@ -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"

View File

@ -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

View File

@ -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)
"""
)

View File

@ -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'
"""
)

View File

@ -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()

View File

@ -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")

View File

@ -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)
)
)

View File

@ -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)

View File

@ -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 = (
"<p>Project Name: "

View File

@ -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,

View File

@ -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"
}
}

View File

@ -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"):

View File

@ -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",

View File

@ -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

View File

@ -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,

View File

@ -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
)

View File

@ -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(

View File

@ -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",

View File

@ -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"

View File

@ -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)

View File

@ -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):

View File

@ -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"},
)

View File

@ -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()

View File

@ -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]

View File

@ -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<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

View File

@ -45,3 +45,8 @@ class TestInit(unittest.TestCase):
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")

View File

@ -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>