Merge branch 'develop' into fixed-transferred-materials-are-not-consumed
This commit is contained in:
commit
2ec133df6d
@ -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"
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
)
|
||||
|
||||
|
||||
|
@ -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},
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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": []
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"charts": [],
|
||||
"content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order Summary\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
|
||||
"content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Card\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
|
||||
"creation": "2020-03-02 17:11:37.032604",
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
@ -402,7 +402,7 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2022-05-31 22:08:19.408223",
|
||||
"modified": "2022-06-15 15:18:57.062935",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Manufacturing",
|
||||
@ -415,39 +415,35 @@
|
||||
"sequence_id": 17.0,
|
||||
"shortcuts": [
|
||||
{
|
||||
"color": "Green",
|
||||
"format": "{} Active",
|
||||
"label": "Item",
|
||||
"link_to": "Item",
|
||||
"restrict_to_domain": "Manufacturing",
|
||||
"stats_filter": "{\n \"disabled\": 0\n}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"color": "Green",
|
||||
"format": "{} Active",
|
||||
"color": "Grey",
|
||||
"doc_view": "List",
|
||||
"label": "BOM",
|
||||
"link_to": "BOM",
|
||||
"restrict_to_domain": "Manufacturing",
|
||||
"stats_filter": "{\n \"is_active\": 1\n}",
|
||||
"stats_filter": "{\"is_active\":[\"=\",1]}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"color": "Yellow",
|
||||
"format": "{} Open",
|
||||
"label": "Work Order",
|
||||
"link_to": "Work Order",
|
||||
"restrict_to_domain": "Manufacturing",
|
||||
"stats_filter": "{ \n \"status\": [\"in\", \n [\"Draft\", \"Not Started\", \"In Process\"]\n ]\n}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"color": "Yellow",
|
||||
"format": "{} Open",
|
||||
"color": "Grey",
|
||||
"doc_view": "List",
|
||||
"label": "Production Plan",
|
||||
"link_to": "Production Plan",
|
||||
"restrict_to_domain": "Manufacturing",
|
||||
"stats_filter": "{ \n \"status\": [\"not in\", [\"Completed\"]]\n}",
|
||||
"stats_filter": "{\"status\":[\"not in\",[\"Closed\",\"Cancelled\",\"Completed\"]]}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"color": "Grey",
|
||||
"doc_view": "List",
|
||||
"label": "Work Order",
|
||||
"link_to": "Work Order",
|
||||
"stats_filter": "{\"status\":[\"not in\",[\"Closed\",\"Cancelled\",\"Completed\"]]}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"color": "Grey",
|
||||
"doc_view": "List",
|
||||
"label": "Job Card",
|
||||
"link_to": "Job Card",
|
||||
"stats_filter": "{\"status\":[\"not in\",[\"Cancelled\",\"Completed\",null]]}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
"""
|
||||
)
|
@ -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'
|
||||
"""
|
||||
)
|
@ -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()
|
@ -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")
|
@ -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)
|
||||
)
|
||||
)
|
@ -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)
|
||||
|
@ -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: "
|
||||
|
@ -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,
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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"):
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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(
|
||||
|
@ -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",
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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"},
|
||||
)
|
||||
|
@ -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()
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user