mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-02 19:59:12 +00:00
Merge pull request #37247 from frappe/version-14-hotfix
chore: release v14
This commit is contained in:
@@ -33,7 +33,7 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.validate_future_closing_vouchers()
|
self.validate_future_closing_vouchers()
|
||||||
self.db_set("gle_processing_status", "In Progress")
|
self.db_set("gle_processing_status", "In Progress")
|
||||||
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
|
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
|
||||||
gle_count = frappe.db.count(
|
gle_count = frappe.db.count(
|
||||||
"GL Entry",
|
"GL Entry",
|
||||||
{"voucher_type": "Period Closing Voucher", "voucher_no": self.name, "is_cancelled": 0},
|
{"voucher_type": "Period Closing Voucher", "voucher_no": self.name, "is_cancelled": 0},
|
||||||
|
|||||||
@@ -518,7 +518,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
selling_price_list = (
|
selling_price_list = (
|
||||||
customer_price_list or customer_group_price_list or profile.get("selling_price_list")
|
customer_price_list or customer_group_price_list or profile.get("selling_price_list")
|
||||||
)
|
)
|
||||||
if customer_currency != profile.get("currency"):
|
if customer_currency and customer_currency != profile.get("currency"):
|
||||||
self.set("currency", customer_currency)
|
self.set("currency", customer_currency)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ def get_report_pdf(doc, consolidated=True):
|
|||||||
filters = get_common_filters(doc)
|
filters = get_common_filters(doc)
|
||||||
|
|
||||||
if doc.report == "General Ledger":
|
if doc.report == "General Ledger":
|
||||||
|
filters.update(get_gl_filters(doc, entry, tax_id, presentation_currency))
|
||||||
col, res = get_soa(filters)
|
col, res = get_soa(filters)
|
||||||
for x in [0, -2, -1]:
|
for x in [0, -2, -1]:
|
||||||
res[x]["account"] = res[x]["account"].replace("'", "")
|
res[x]["account"] = res[x]["account"].replace("'", "")
|
||||||
@@ -142,7 +143,8 @@ def get_gl_filters(doc, entry, tax_id, presentation_currency):
|
|||||||
def get_ar_filters(doc, entry):
|
def get_ar_filters(doc, entry):
|
||||||
return {
|
return {
|
||||||
"report_date": doc.posting_date if doc.posting_date else None,
|
"report_date": doc.posting_date if doc.posting_date else None,
|
||||||
"customer": entry.customer,
|
"party_type": "Customer",
|
||||||
|
"party": entry.customer,
|
||||||
"customer_name": entry.customer_name if entry.customer_name else None,
|
"customer_name": entry.customer_name if entry.customer_name else None,
|
||||||
"payment_terms_template": doc.payment_terms_template if doc.payment_terms_template else None,
|
"payment_terms_template": doc.payment_terms_template if doc.payment_terms_template else None,
|
||||||
"sales_partner": doc.sales_partner if doc.sales_partner else None,
|
"sales_partner": doc.sales_partner if doc.sales_partner else None,
|
||||||
|
|||||||
@@ -1801,6 +1801,10 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_outstanding_amount_after_advance_payment_entry_cancellation(self):
|
def test_outstanding_amount_after_advance_payment_entry_cancellation(self):
|
||||||
|
"""Test impact of advance PE submission/cancellation on SI and SO."""
|
||||||
|
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||||
|
|
||||||
|
sales_order = make_sales_order(item_code="138-CMS Shoe", qty=1, price_list_rate=500)
|
||||||
pe = frappe.get_doc(
|
pe = frappe.get_doc(
|
||||||
{
|
{
|
||||||
"doctype": "Payment Entry",
|
"doctype": "Payment Entry",
|
||||||
@@ -1820,10 +1824,25 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
"paid_to": "_Test Cash - _TC",
|
"paid_to": "_Test Cash - _TC",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
pe.append(
|
||||||
|
"references",
|
||||||
|
{
|
||||||
|
"reference_doctype": "Sales Order",
|
||||||
|
"reference_name": sales_order.name,
|
||||||
|
"total_amount": sales_order.grand_total,
|
||||||
|
"outstanding_amount": sales_order.grand_total,
|
||||||
|
"allocated_amount": 300,
|
||||||
|
},
|
||||||
|
)
|
||||||
pe.insert()
|
pe.insert()
|
||||||
pe.submit()
|
pe.submit()
|
||||||
|
|
||||||
|
sales_order.reload()
|
||||||
|
self.assertEqual(sales_order.advance_paid, 300)
|
||||||
|
|
||||||
si = frappe.copy_doc(test_records[0])
|
si = frappe.copy_doc(test_records[0])
|
||||||
|
si.items[0].sales_order = sales_order.name
|
||||||
|
si.items[0].so_detail = sales_order.get("items")[0].name
|
||||||
si.is_pos = 0
|
si.is_pos = 0
|
||||||
si.append(
|
si.append(
|
||||||
"advances",
|
"advances",
|
||||||
@@ -1831,6 +1850,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
"doctype": "Sales Invoice Advance",
|
"doctype": "Sales Invoice Advance",
|
||||||
"reference_type": "Payment Entry",
|
"reference_type": "Payment Entry",
|
||||||
"reference_name": pe.name,
|
"reference_name": pe.name,
|
||||||
|
"reference_row": pe.references[0].name,
|
||||||
"advance_amount": 300,
|
"advance_amount": 300,
|
||||||
"allocated_amount": 300,
|
"allocated_amount": 300,
|
||||||
"remarks": pe.remarks,
|
"remarks": pe.remarks,
|
||||||
@@ -1839,7 +1859,13 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
si.insert()
|
si.insert()
|
||||||
si.submit()
|
si.submit()
|
||||||
|
|
||||||
si.load_from_db()
|
si.reload()
|
||||||
|
pe.reload()
|
||||||
|
sales_order.reload()
|
||||||
|
|
||||||
|
# Check if SO is unlinked/replaced by SI in PE & if SO advance paid is 0
|
||||||
|
self.assertEqual(pe.references[0].reference_name, si.name)
|
||||||
|
self.assertEqual(sales_order.advance_paid, 0.0)
|
||||||
|
|
||||||
# check outstanding after advance allocation
|
# check outstanding after advance allocation
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@@ -1847,11 +1873,9 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
flt(si.rounded_total - si.total_advance, si.precision("outstanding_amount")),
|
flt(si.rounded_total - si.total_advance, si.precision("outstanding_amount")),
|
||||||
)
|
)
|
||||||
|
|
||||||
# added to avoid Document has been modified exception
|
|
||||||
pe = frappe.get_doc("Payment Entry", pe.name)
|
|
||||||
pe.cancel()
|
pe.cancel()
|
||||||
|
si.reload()
|
||||||
|
|
||||||
si.load_from_db()
|
|
||||||
# check outstanding after advance cancellation
|
# check outstanding after advance cancellation
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
flt(si.outstanding_amount),
|
flt(si.outstanding_amount),
|
||||||
|
|||||||
@@ -108,11 +108,8 @@ frappe.query_reports["Accounts Payable"] = {
|
|||||||
},
|
},
|
||||||
on_change: () => {
|
on_change: () => {
|
||||||
frappe.query_report.set_filter_value('party', "");
|
frappe.query_report.set_filter_value('party', "");
|
||||||
let party_type = frappe.query_report.get_filter_value('party_type');
|
|
||||||
frappe.query_report.toggle_filter_display('supplier_group', frappe.query_report.get_filter_value('party_type') !== "Supplier");
|
frappe.query_report.toggle_filter_display('supplier_group', frappe.query_report.get_filter_value('party_type') !== "Supplier");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"party",
|
"fieldname":"party",
|
||||||
|
|||||||
@@ -72,10 +72,27 @@ frappe.query_reports["Accounts Payable Summary"] = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"supplier",
|
"fieldname": "party_type",
|
||||||
"label": __("Supplier"),
|
"label": __("Party Type"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Supplier"
|
"options": "Party Type",
|
||||||
|
get_query: () => {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
'account_type': 'Payable'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
on_change: () => {
|
||||||
|
frappe.query_report.set_filter_value('party', "");
|
||||||
|
frappe.query_report.toggle_filter_display('supplier_group', frappe.query_report.get_filter_value('party_type') !== "Supplier");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"party",
|
||||||
|
"label": __("Party"),
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"options": "party_type",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"payment_terms_template",
|
"fieldname":"payment_terms_template",
|
||||||
|
|||||||
@@ -52,9 +52,7 @@ frappe.query_reports["Accounts Receivable"] = {
|
|||||||
},
|
},
|
||||||
on_change: () => {
|
on_change: () => {
|
||||||
frappe.query_report.set_filter_value('party', "");
|
frappe.query_report.set_filter_value('party', "");
|
||||||
let party_type = frappe.query_report.get_filter_value('party_type');
|
|
||||||
frappe.query_report.toggle_filter_display('customer_group', frappe.query_report.get_filter_value('party_type') !== "Customer");
|
frappe.query_report.toggle_filter_display('customer_group', frappe.query_report.get_filter_value('party_type') !== "Customer");
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -72,10 +72,28 @@ frappe.query_reports["Accounts Receivable Summary"] = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"customer",
|
"fieldname": "party_type",
|
||||||
"label": __("Customer"),
|
"label": __("Party Type"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Customer"
|
"options": "Party Type",
|
||||||
|
"Default": "Customer",
|
||||||
|
get_query: () => {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
'account_type': 'Receivable'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
on_change: () => {
|
||||||
|
frappe.query_report.set_filter_value('party', "");
|
||||||
|
frappe.query_report.toggle_filter_display('customer_group', frappe.query_report.get_filter_value('party_type') !== "Customer");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"party",
|
||||||
|
"label": __("Party"),
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"options": "party_type",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"customer_group",
|
"fieldname":"customer_group",
|
||||||
|
|||||||
@@ -99,6 +99,12 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
"label": __("Include Default Book Entries"),
|
"label": __("Include Default Book Entries"),
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"default": 1
|
"default": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "show_net_values",
|
||||||
|
"label": __("Show net values in opening and closing columns"),
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"default": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"formatter": erpnext.financial_statements.formatter,
|
"formatter": erpnext.financial_statements.formatter,
|
||||||
|
|||||||
@@ -120,7 +120,9 @@ def get_data(filters):
|
|||||||
ignore_opening_entries=True,
|
ignore_opening_entries=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
calculate_values(accounts, gl_entries_by_account, opening_balances)
|
calculate_values(
|
||||||
|
accounts, gl_entries_by_account, opening_balances, filters.get("show_net_values")
|
||||||
|
)
|
||||||
accumulate_values_into_parents(accounts, accounts_by_name)
|
accumulate_values_into_parents(accounts, accounts_by_name)
|
||||||
|
|
||||||
data = prepare_data(accounts, filters, parent_children_map, company_currency)
|
data = prepare_data(accounts, filters, parent_children_map, company_currency)
|
||||||
@@ -310,7 +312,7 @@ def get_opening_balance(
|
|||||||
return gle
|
return gle
|
||||||
|
|
||||||
|
|
||||||
def calculate_values(accounts, gl_entries_by_account, opening_balances):
|
def calculate_values(accounts, gl_entries_by_account, opening_balances, show_net_values):
|
||||||
init = {
|
init = {
|
||||||
"opening_debit": 0.0,
|
"opening_debit": 0.0,
|
||||||
"opening_credit": 0.0,
|
"opening_credit": 0.0,
|
||||||
@@ -335,7 +337,8 @@ def calculate_values(accounts, gl_entries_by_account, opening_balances):
|
|||||||
d["closing_debit"] = d["opening_debit"] + d["debit"]
|
d["closing_debit"] = d["opening_debit"] + d["debit"]
|
||||||
d["closing_credit"] = d["opening_credit"] + d["credit"]
|
d["closing_credit"] = d["opening_credit"] + d["credit"]
|
||||||
|
|
||||||
prepare_opening_closing(d)
|
if show_net_values:
|
||||||
|
prepare_opening_closing(d)
|
||||||
|
|
||||||
|
|
||||||
def calculate_total_row(accounts, company_currency):
|
def calculate_total_row(accounts, company_currency):
|
||||||
@@ -375,7 +378,7 @@ def prepare_data(accounts, filters, parent_children_map, company_currency):
|
|||||||
|
|
||||||
for d in accounts:
|
for d in accounts:
|
||||||
# Prepare opening closing for group account
|
# Prepare opening closing for group account
|
||||||
if parent_children_map.get(d.account):
|
if parent_children_map.get(d.account) and filters.get("show_net_values"):
|
||||||
prepare_opening_closing(d)
|
prepare_opening_closing(d)
|
||||||
|
|
||||||
has_value = False
|
has_value = False
|
||||||
|
|||||||
@@ -560,6 +560,10 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
|
|||||||
"""
|
"""
|
||||||
jv_detail = journal_entry.get("accounts", {"name": d["voucher_detail_no"]})[0]
|
jv_detail = journal_entry.get("accounts", {"name": d["voucher_detail_no"]})[0]
|
||||||
|
|
||||||
|
# Update Advance Paid in SO/PO since they might be getting unlinked
|
||||||
|
if jv_detail.get("reference_type") in ("Sales Order", "Purchase Order"):
|
||||||
|
frappe.get_doc(jv_detail.reference_type, jv_detail.reference_name).set_total_advance_paid()
|
||||||
|
|
||||||
if flt(d["unadjusted_amount"]) - flt(d["allocated_amount"]) != 0:
|
if flt(d["unadjusted_amount"]) - flt(d["allocated_amount"]) != 0:
|
||||||
# adjust the unreconciled balance
|
# adjust the unreconciled balance
|
||||||
amount_in_account_currency = flt(d["unadjusted_amount"]) - flt(d["allocated_amount"])
|
amount_in_account_currency = flt(d["unadjusted_amount"]) - flt(d["allocated_amount"])
|
||||||
@@ -625,6 +629,13 @@ def update_reference_in_payment_entry(
|
|||||||
|
|
||||||
if d.voucher_detail_no:
|
if d.voucher_detail_no:
|
||||||
existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0]
|
existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0]
|
||||||
|
|
||||||
|
# Update Advance Paid in SO/PO since they are getting unlinked
|
||||||
|
if existing_row.get("reference_doctype") in ("Sales Order", "Purchase Order"):
|
||||||
|
frappe.get_doc(
|
||||||
|
existing_row.reference_doctype, existing_row.reference_name
|
||||||
|
).set_total_advance_paid()
|
||||||
|
|
||||||
original_row = existing_row.as_dict().copy()
|
original_row = existing_row.as_dict().copy()
|
||||||
existing_row.update(reference_details)
|
existing_row.update(reference_details)
|
||||||
|
|
||||||
|
|||||||
@@ -655,7 +655,7 @@ class SubcontractingController(StockController):
|
|||||||
{
|
{
|
||||||
"item_code": item.rm_item_code,
|
"item_code": item.rm_item_code,
|
||||||
"warehouse": self.supplier_warehouse,
|
"warehouse": self.supplier_warehouse,
|
||||||
"actual_qty": -1 * flt(item.consumed_qty),
|
"actual_qty": -1 * flt(item.consumed_qty, item.precision("consumed_qty")),
|
||||||
"dependant_sle_voucher_detail_no": item.reference_name,
|
"dependant_sle_voucher_detail_no": item.reference_name,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ class TestWebsiteItem(unittest.TestCase):
|
|||||||
# check if stock details are fetched and item not in stock with warehouse set
|
# check if stock details are fetched and item not in stock with warehouse set
|
||||||
data = get_product_info_for_website(item_code, skip_quotation_creation=True)
|
data = get_product_info_for_website(item_code, skip_quotation_creation=True)
|
||||||
self.assertFalse(bool(data.product_info["in_stock"]))
|
self.assertFalse(bool(data.product_info["in_stock"]))
|
||||||
self.assertEqual(data.product_info["stock_qty"][0][0], 0)
|
self.assertEqual(data.product_info["stock_qty"], 0)
|
||||||
|
|
||||||
# disable show stock availability
|
# disable show stock availability
|
||||||
setup_e_commerce_settings({"show_stock_availability": 0})
|
setup_e_commerce_settings({"show_stock_availability": 0})
|
||||||
@@ -355,7 +355,7 @@ class TestWebsiteItem(unittest.TestCase):
|
|||||||
# check if stock details are fetched and item is in stock with warehouse set
|
# check if stock details are fetched and item is in stock with warehouse set
|
||||||
data = get_product_info_for_website(item_code, skip_quotation_creation=True)
|
data = get_product_info_for_website(item_code, skip_quotation_creation=True)
|
||||||
self.assertTrue(bool(data.product_info["in_stock"]))
|
self.assertTrue(bool(data.product_info["in_stock"]))
|
||||||
self.assertEqual(data.product_info["stock_qty"][0][0], 2)
|
self.assertEqual(data.product_info["stock_qty"], 2)
|
||||||
|
|
||||||
# unset warehouse
|
# unset warehouse
|
||||||
frappe.db.set_value("Website Item", {"item_code": item_code}, "website_warehouse", "")
|
frappe.db.set_value("Website Item", {"item_code": item_code}, "website_warehouse", "")
|
||||||
@@ -364,7 +364,7 @@ class TestWebsiteItem(unittest.TestCase):
|
|||||||
# (even though it has stock in some warehouse)
|
# (even though it has stock in some warehouse)
|
||||||
data = get_product_info_for_website(item_code, skip_quotation_creation=True)
|
data = get_product_info_for_website(item_code, skip_quotation_creation=True)
|
||||||
self.assertFalse(bool(data.product_info["in_stock"]))
|
self.assertFalse(bool(data.product_info["in_stock"]))
|
||||||
self.assertFalse(bool(data.product_info["stock_qty"]))
|
self.assertFalse(data.product_info["stock_qty"])
|
||||||
|
|
||||||
# disable show stock availability
|
# disable show stock availability
|
||||||
setup_e_commerce_settings({"show_stock_availability": 0})
|
setup_e_commerce_settings({"show_stock_availability": 0})
|
||||||
|
|||||||
@@ -5,12 +5,6 @@ frappe.ui.form.on('Website Item', {
|
|||||||
onload: (frm) => {
|
onload: (frm) => {
|
||||||
// should never check Private
|
// should never check Private
|
||||||
frm.fields_dict["website_image"].df.is_private = 0;
|
frm.fields_dict["website_image"].df.is_private = 0;
|
||||||
|
|
||||||
frm.set_query("website_warehouse", () => {
|
|
||||||
return {
|
|
||||||
filters: {"is_group": 0}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: (frm) => {
|
refresh: (frm) => {
|
||||||
|
|||||||
@@ -135,7 +135,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Show Stock availability based on this warehouse.",
|
"description": "Show Stock availability based on this warehouse. If the parent warehouse is selected, then the system will display the consolidated available quantity of all child warehouses.",
|
||||||
"fieldname": "website_warehouse",
|
"fieldname": "website_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"ignore_user_permissions": 1,
|
"ignore_user_permissions": 1,
|
||||||
@@ -348,7 +348,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"make_attachments_public": 1,
|
"make_attachments_public": 1,
|
||||||
"modified": "2022-09-30 04:01:52.090732",
|
"modified": "2023-09-12 14:19:22.822689",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "E-commerce",
|
"module": "E-commerce",
|
||||||
"name": "Website Item",
|
"name": "Website Item",
|
||||||
|
|||||||
@@ -259,6 +259,10 @@ class ProductQuery:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_stock_availability(self, item):
|
def get_stock_availability(self, item):
|
||||||
|
from erpnext.templates.pages.wishlist import (
|
||||||
|
get_stock_availability as get_stock_availability_from_template,
|
||||||
|
)
|
||||||
|
|
||||||
"""Modify item object and add stock details."""
|
"""Modify item object and add stock details."""
|
||||||
item.in_stock = False
|
item.in_stock = False
|
||||||
warehouse = item.get("website_warehouse")
|
warehouse = item.get("website_warehouse")
|
||||||
@@ -274,11 +278,7 @@ class ProductQuery:
|
|||||||
else:
|
else:
|
||||||
item.in_stock = True
|
item.in_stock = True
|
||||||
elif warehouse:
|
elif warehouse:
|
||||||
# stock item and has warehouse
|
item.in_stock = get_stock_availability_from_template(item.item_code, warehouse)
|
||||||
actual_qty = frappe.db.get_value(
|
|
||||||
"Bin", {"item_code": item.item_code, "warehouse": item.get("website_warehouse")}, "actual_qty"
|
|
||||||
)
|
|
||||||
item.in_stock = bool(flt(actual_qty))
|
|
||||||
|
|
||||||
def get_cart_items(self):
|
def get_cart_items(self):
|
||||||
customer = get_customer(silent=True)
|
customer = get_customer(silent=True)
|
||||||
|
|||||||
@@ -111,8 +111,8 @@ def place_order():
|
|||||||
item_stock = get_web_item_qty_in_stock(item.item_code, "website_warehouse")
|
item_stock = get_web_item_qty_in_stock(item.item_code, "website_warehouse")
|
||||||
if not cint(item_stock.in_stock):
|
if not cint(item_stock.in_stock):
|
||||||
throw(_("{0} Not in Stock").format(item.item_code))
|
throw(_("{0} Not in Stock").format(item.item_code))
|
||||||
if item.qty > item_stock.stock_qty[0][0]:
|
if item.qty > item_stock.stock_qty:
|
||||||
throw(_("Only {0} in Stock for item {1}").format(item_stock.stock_qty[0][0], item.item_code))
|
throw(_("Only {0} in Stock for item {1}").format(item_stock.stock_qty, item.item_code))
|
||||||
|
|
||||||
sales_order.flags.ignore_permissions = True
|
sales_order.flags.ignore_permissions = True
|
||||||
sales_order.insert()
|
sales_order.insert()
|
||||||
@@ -150,6 +150,10 @@ def update_cart(item_code, qty, additional_notes=None, with_items=False):
|
|||||||
empty_card = True
|
empty_card = True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
warehouse = frappe.get_cached_value(
|
||||||
|
"Website Item", {"item_code": item_code}, "website_warehouse"
|
||||||
|
)
|
||||||
|
|
||||||
quotation_items = quotation.get("items", {"item_code": item_code})
|
quotation_items = quotation.get("items", {"item_code": item_code})
|
||||||
if not quotation_items:
|
if not quotation_items:
|
||||||
quotation.append(
|
quotation.append(
|
||||||
@@ -159,11 +163,13 @@ def update_cart(item_code, qty, additional_notes=None, with_items=False):
|
|||||||
"item_code": item_code,
|
"item_code": item_code,
|
||||||
"qty": qty,
|
"qty": qty,
|
||||||
"additional_notes": additional_notes,
|
"additional_notes": additional_notes,
|
||||||
|
"warehouse": warehouse,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
quotation_items[0].qty = qty
|
quotation_items[0].qty = qty
|
||||||
quotation_items[0].additional_notes = additional_notes
|
quotation_items[0].additional_notes = additional_notes
|
||||||
|
quotation_items[0].warehouse = warehouse
|
||||||
|
|
||||||
apply_cart_settings(quotation=quotation)
|
apply_cart_settings(quotation=quotation)
|
||||||
|
|
||||||
@@ -322,6 +328,10 @@ def decorate_quotation_doc(doc):
|
|||||||
fields = fields[2:]
|
fields = fields[2:]
|
||||||
|
|
||||||
d.update(frappe.db.get_value("Website Item", {"item_code": item_code}, fields, as_dict=True))
|
d.update(frappe.db.get_value("Website Item", {"item_code": item_code}, fields, as_dict=True))
|
||||||
|
website_warehouse = frappe.get_cached_value(
|
||||||
|
"Website Item", {"item_code": item_code}, "website_warehouse"
|
||||||
|
)
|
||||||
|
d.warehouse = website_warehouse
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,8 @@ def get_attributes_and_values(item_code):
|
|||||||
|
|
||||||
@frappe.whitelist(allow_guest=True)
|
@frappe.whitelist(allow_guest=True)
|
||||||
def get_next_attribute_and_values(item_code, selected_attributes):
|
def get_next_attribute_and_values(item_code, selected_attributes):
|
||||||
|
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
|
||||||
|
|
||||||
"""Find the count of Items that match the selected attributes.
|
"""Find the count of Items that match the selected attributes.
|
||||||
Also, find the attribute values that are not applicable for further searching.
|
Also, find the attribute values that are not applicable for further searching.
|
||||||
If less than equal to 10 items are found, return item_codes of those items.
|
If less than equal to 10 items are found, return item_codes of those items.
|
||||||
@@ -168,7 +170,7 @@ def get_next_attribute_and_values(item_code, selected_attributes):
|
|||||||
product_info = None
|
product_info = None
|
||||||
|
|
||||||
product_id = ""
|
product_id = ""
|
||||||
website_warehouse = ""
|
warehouse = ""
|
||||||
if exact_match or filtered_items:
|
if exact_match or filtered_items:
|
||||||
if exact_match and len(exact_match) == 1:
|
if exact_match and len(exact_match) == 1:
|
||||||
product_id = exact_match[0]
|
product_id = exact_match[0]
|
||||||
@@ -176,16 +178,19 @@ def get_next_attribute_and_values(item_code, selected_attributes):
|
|||||||
product_id = list(filtered_items)[0]
|
product_id = list(filtered_items)[0]
|
||||||
|
|
||||||
if product_id:
|
if product_id:
|
||||||
website_warehouse = frappe.get_cached_value(
|
warehouse = frappe.get_cached_value(
|
||||||
"Website Item", {"item_code": product_id}, "website_warehouse"
|
"Website Item", {"item_code": product_id}, "website_warehouse"
|
||||||
)
|
)
|
||||||
|
|
||||||
available_qty = 0.0
|
available_qty = 0.0
|
||||||
if website_warehouse:
|
if warehouse and frappe.get_cached_value("Warehouse", warehouse, "is_group") == 1:
|
||||||
available_qty = flt(
|
warehouses = get_child_warehouses(warehouse)
|
||||||
frappe.db.get_value(
|
else:
|
||||||
"Bin", {"item_code": product_id, "warehouse": website_warehouse}, "actual_qty"
|
warehouses = [warehouse] if warehouse else []
|
||||||
)
|
|
||||||
|
for warehouse in warehouses:
|
||||||
|
available_qty += flt(
|
||||||
|
frappe.db.get_value("Bin", {"item_code": product_id, "warehouse": warehouse}, "actual_qty")
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1039,6 +1039,59 @@ class TestProductionPlan(FrappeTestCase):
|
|||||||
|
|
||||||
self.assertEqual(after_qty, before_qty)
|
self.assertEqual(after_qty, before_qty)
|
||||||
|
|
||||||
|
def test_resered_qty_for_production_plan_for_work_order(self):
|
||||||
|
from erpnext.stock.utils import get_or_make_bin
|
||||||
|
|
||||||
|
bin_name = get_or_make_bin("Raw Material Item 1", "_Test Warehouse - _TC")
|
||||||
|
before_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
|
||||||
|
|
||||||
|
pln = create_production_plan(item_code="Test Production Item 1")
|
||||||
|
|
||||||
|
bin_name = get_or_make_bin("Raw Material Item 1", "_Test Warehouse - _TC")
|
||||||
|
after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
|
||||||
|
|
||||||
|
self.assertEqual(after_qty - before_qty, 1)
|
||||||
|
|
||||||
|
pln.make_work_order()
|
||||||
|
|
||||||
|
work_orders = []
|
||||||
|
for row in frappe.get_all("Work Order", filters={"production_plan": pln.name}, fields=["name"]):
|
||||||
|
wo_doc = frappe.get_doc("Work Order", row.name)
|
||||||
|
wo_doc.source_warehouse = "_Test Warehouse - _TC"
|
||||||
|
wo_doc.wip_warehouse = "_Test Warehouse 1 - _TC"
|
||||||
|
wo_doc.fg_warehouse = "_Test Warehouse - _TC"
|
||||||
|
for d in wo_doc.required_items:
|
||||||
|
d.source_warehouse = "_Test Warehouse - _TC"
|
||||||
|
make_stock_entry(
|
||||||
|
item_code=d.item_code,
|
||||||
|
qty=d.required_qty,
|
||||||
|
rate=100,
|
||||||
|
target="_Test Warehouse - _TC",
|
||||||
|
)
|
||||||
|
|
||||||
|
wo_doc.submit()
|
||||||
|
work_orders.append(wo_doc)
|
||||||
|
|
||||||
|
bin_name = get_or_make_bin("Raw Material Item 1", "_Test Warehouse - _TC")
|
||||||
|
after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
|
||||||
|
|
||||||
|
self.assertEqual(after_qty, before_qty)
|
||||||
|
|
||||||
|
rm_work_order = None
|
||||||
|
for wo_doc in work_orders:
|
||||||
|
for d in wo_doc.required_items:
|
||||||
|
if d.item_code == "Raw Material Item 1":
|
||||||
|
rm_work_order = wo_doc
|
||||||
|
break
|
||||||
|
|
||||||
|
if rm_work_order:
|
||||||
|
s = frappe.get_doc(make_se_from_wo(rm_work_order.name, "Material Transfer for Manufacture", 1))
|
||||||
|
s.submit()
|
||||||
|
bin_name = get_or_make_bin("Raw Material Item 1", "_Test Warehouse - _TC")
|
||||||
|
after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
|
||||||
|
|
||||||
|
self.assertEqual(after_qty, before_qty)
|
||||||
|
|
||||||
def test_resered_qty_for_production_plan_for_material_requests_with_multi_UOM(self):
|
def test_resered_qty_for_production_plan_for_material_requests_with_multi_UOM(self):
|
||||||
from erpnext.stock.utils import get_or_make_bin
|
from erpnext.stock.utils import get_or_make_bin
|
||||||
|
|
||||||
|
|||||||
@@ -1497,16 +1497,17 @@ def get_reserved_qty_for_production(
|
|||||||
wo = frappe.qb.DocType("Work Order")
|
wo = frappe.qb.DocType("Work Order")
|
||||||
wo_item = frappe.qb.DocType("Work Order Item")
|
wo_item = frappe.qb.DocType("Work Order Item")
|
||||||
|
|
||||||
|
if check_production_plan:
|
||||||
|
qty_field = wo_item.required_qty
|
||||||
|
else:
|
||||||
|
qty_field = Case()
|
||||||
|
qty_field = qty_field.when(wo.skip_transfer == 0, wo_item.required_qty - wo_item.transferred_qty)
|
||||||
|
qty_field = qty_field.else_(wo_item.required_qty - wo_item.consumed_qty)
|
||||||
|
|
||||||
query = (
|
query = (
|
||||||
frappe.qb.from_(wo)
|
frappe.qb.from_(wo)
|
||||||
.from_(wo_item)
|
.from_(wo_item)
|
||||||
.select(
|
.select(Sum(qty_field))
|
||||||
Sum(
|
|
||||||
Case()
|
|
||||||
.when(wo.skip_transfer == 0, wo_item.required_qty - wo_item.transferred_qty)
|
|
||||||
.else_(wo_item.required_qty - wo_item.consumed_qty)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.where(
|
.where(
|
||||||
(wo_item.item_code == item_code)
|
(wo_item.item_code == item_code)
|
||||||
& (wo_item.parent == wo.name)
|
& (wo_item.parent == wo.name)
|
||||||
|
|||||||
@@ -269,6 +269,7 @@ erpnext.patches.v13_0.show_hr_payroll_deprecation_warning
|
|||||||
erpnext.patches.v13_0.reset_corrupt_defaults
|
erpnext.patches.v13_0.reset_corrupt_defaults
|
||||||
erpnext.patches.v13_0.create_accounting_dimensions_for_asset_repair
|
erpnext.patches.v13_0.create_accounting_dimensions_for_asset_repair
|
||||||
erpnext.patches.v14_0.update_reference_due_date_in_journal_entry
|
erpnext.patches.v14_0.update_reference_due_date_in_journal_entry
|
||||||
|
erpnext.patches.v14_0.france_depreciation_warning
|
||||||
|
|
||||||
[post_model_sync]
|
[post_model_sync]
|
||||||
execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings')
|
execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings')
|
||||||
|
|||||||
12
erpnext/patches/v14_0/france_depreciation_warning.py
Normal file
12
erpnext/patches/v14_0/france_depreciation_warning.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import click
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
if "erpnext_france" in frappe.get_installed_apps():
|
||||||
|
return
|
||||||
|
click.secho(
|
||||||
|
"Feature for region France will be remove in version-15 and moved to a separate app\n"
|
||||||
|
"Please install the app to continue using the regionnal France features: https://github.com/scopen-coop/erpnext_france.git",
|
||||||
|
fg="yellow",
|
||||||
|
)
|
||||||
@@ -67,6 +67,7 @@ class Project(Document):
|
|||||||
tmp_task_details.append(template_task_details)
|
tmp_task_details.append(template_task_details)
|
||||||
task = self.create_task_from_template(template_task_details)
|
task = self.create_task_from_template(template_task_details)
|
||||||
project_tasks.append(task)
|
project_tasks.append(task)
|
||||||
|
|
||||||
self.dependency_mapping(tmp_task_details, project_tasks)
|
self.dependency_mapping(tmp_task_details, project_tasks)
|
||||||
|
|
||||||
def create_task_from_template(self, task_details):
|
def create_task_from_template(self, task_details):
|
||||||
@@ -105,36 +106,28 @@ class Project(Document):
|
|||||||
|
|
||||||
def dependency_mapping(self, template_tasks, project_tasks):
|
def dependency_mapping(self, template_tasks, project_tasks):
|
||||||
for project_task in project_tasks:
|
for project_task in project_tasks:
|
||||||
if project_task.get("template_task"):
|
template_task = frappe.get_doc("Task", project_task.template_task)
|
||||||
template_task = frappe.get_doc("Task", project_task.template_task)
|
|
||||||
else:
|
|
||||||
template_task = list(filter(lambda x: x.subject == project_task.subject, template_tasks))[0]
|
|
||||||
template_task = frappe.get_doc("Task", template_task.name)
|
|
||||||
|
|
||||||
self.check_depends_on_value(template_task, project_task, project_tasks)
|
self.check_depends_on_value(template_task, project_task, project_tasks)
|
||||||
self.check_for_parent_tasks(template_task, project_task, project_tasks)
|
self.check_for_parent_tasks(template_task, project_task, project_tasks)
|
||||||
|
|
||||||
def check_depends_on_value(self, template_task, project_task, project_tasks):
|
def check_depends_on_value(self, template_task, project_task, project_tasks):
|
||||||
if template_task.get("depends_on") and not project_task.get("depends_on"):
|
if template_task.get("depends_on") and not project_task.get("depends_on"):
|
||||||
|
project_template_map = {pt.template_task: pt for pt in project_tasks}
|
||||||
|
|
||||||
for child_task in template_task.get("depends_on"):
|
for child_task in template_task.get("depends_on"):
|
||||||
child_task_subject = frappe.db.get_value("Task", child_task.task, "subject")
|
if project_template_map and project_template_map.get(child_task.task):
|
||||||
corresponding_project_task = list(
|
|
||||||
filter(lambda x: x.subject == child_task_subject, project_tasks)
|
|
||||||
)
|
|
||||||
if len(corresponding_project_task):
|
|
||||||
project_task.reload() # reload, as it might have been updated in the previous iteration
|
project_task.reload() # reload, as it might have been updated in the previous iteration
|
||||||
project_task.append("depends_on", {"task": corresponding_project_task[0].name})
|
project_task.append("depends_on", {"task": project_template_map.get(child_task.task).name})
|
||||||
project_task.save()
|
project_task.save()
|
||||||
|
|
||||||
def check_for_parent_tasks(self, template_task, project_task, project_tasks):
|
def check_for_parent_tasks(self, template_task, project_task, project_tasks):
|
||||||
if template_task.get("parent_task") and not project_task.get("parent_task"):
|
if template_task.get("parent_task") and not project_task.get("parent_task"):
|
||||||
parent_task_subject = frappe.db.get_value("Task", template_task.get("parent_task"), "subject")
|
for pt in project_tasks:
|
||||||
corresponding_project_task = list(
|
if pt.template_task == template_task.parent_task:
|
||||||
filter(lambda x: x.subject == parent_task_subject, project_tasks)
|
project_task.parent_task = pt.name
|
||||||
)
|
project_task.save()
|
||||||
if len(corresponding_project_task):
|
break
|
||||||
project_task.parent_task = corresponding_project_task[0].name
|
|
||||||
project_task.save()
|
|
||||||
|
|
||||||
def is_row_updated(self, row, existing_task_data, fields):
|
def is_row_updated(self, row, existing_task_data, fields):
|
||||||
if self.get("__islocal") or not existing_task_data:
|
if self.get("__islocal") or not existing_task_data:
|
||||||
|
|||||||
@@ -138,7 +138,9 @@ class DeliveryNote(SellingController):
|
|||||||
self.validate_uom_is_integer("stock_uom", "stock_qty")
|
self.validate_uom_is_integer("stock_uom", "stock_qty")
|
||||||
self.validate_uom_is_integer("uom", "qty")
|
self.validate_uom_is_integer("uom", "qty")
|
||||||
self.validate_with_previous_doc()
|
self.validate_with_previous_doc()
|
||||||
self.validate_duplicate_serial_nos()
|
|
||||||
|
if self.get("_action") == "submit":
|
||||||
|
self.validate_duplicate_serial_nos()
|
||||||
|
|
||||||
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
|
||||||
|
|
||||||
|
|||||||
@@ -1234,14 +1234,10 @@ class TestDeliveryNote(FrappeTestCase):
|
|||||||
)
|
)
|
||||||
dn.items[0].serial_no = "\n".join(serial_nos[:2])
|
dn.items[0].serial_no = "\n".join(serial_nos[:2])
|
||||||
dn.append("items", dn.items[0].as_dict())
|
dn.append("items", dn.items[0].as_dict())
|
||||||
|
dn.save()
|
||||||
|
|
||||||
# Test - 1: ValidationError should be raised
|
# Test - 1: ValidationError should be raised
|
||||||
self.assertRaises(frappe.ValidationError, dn.save)
|
self.assertRaises(frappe.ValidationError, dn.submit)
|
||||||
|
|
||||||
# Step - 4: Submit Delivery Note with unique Serial Nos
|
|
||||||
dn.items[1].serial_no = "\n".join(serial_nos[2:])
|
|
||||||
dn.save()
|
|
||||||
dn.submit()
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
|
|||||||
@@ -218,7 +218,8 @@ frappe.ui.form.on('Material Request', {
|
|||||||
plc_conversion_rate: 1,
|
plc_conversion_rate: 1,
|
||||||
rate: item.rate,
|
rate: item.rate,
|
||||||
uom: item.uom,
|
uom: item.uom,
|
||||||
conversion_factor: item.conversion_factor
|
conversion_factor: item.conversion_factor,
|
||||||
|
project: item.project,
|
||||||
},
|
},
|
||||||
overwrite_warehouse: overwrite_warehouse
|
overwrite_warehouse: overwrite_warehouse
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1392,6 +1392,9 @@ def get_default_bom(item_code=None):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_valuation_rate(item_code, company, warehouse=None):
|
def get_valuation_rate(item_code, company, warehouse=None):
|
||||||
|
if frappe.get_cached_value("Warehouse", warehouse, "is_group"):
|
||||||
|
return {"valuation_rate": 0.0}
|
||||||
|
|
||||||
item = get_item_defaults(item_code, company)
|
item = get_item_defaults(item_code, company)
|
||||||
item_group = get_item_group_defaults(item_code, company)
|
item_group = get_item_group_defaults(item_code, company)
|
||||||
brand = get_brand_defaults(item_code, company)
|
brand = get_brand_defaults(item_code, company)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
|
||||||
const DIFFERNCE_FIELD_NAMES = [
|
const DIFFERENCE_FIELD_NAMES = [
|
||||||
'difference_in_qty',
|
'difference_in_qty',
|
||||||
'fifo_qty_diff',
|
'fifo_qty_diff',
|
||||||
'fifo_value_diff',
|
'fifo_value_diff',
|
||||||
@@ -37,7 +37,7 @@ frappe.query_reports['Stock Ledger Invariant Check'] = {
|
|||||||
|
|
||||||
formatter (value, row, column, data, default_formatter) {
|
formatter (value, row, column, data, default_formatter) {
|
||||||
value = default_formatter(value, row, column, data);
|
value = default_formatter(value, row, column, data);
|
||||||
if (DIFFERNCE_FIELD_NAMES.includes(column.fieldname) && Math.abs(data[column.fieldname]) > 0.001) {
|
if (DIFFERENCE_FIELD_NAMES.includes(column.fieldname) && Math.abs(data[column.fieldname]) > 0.001) {
|
||||||
value = '<span style="color:red">' + value + '</span>';
|
value = '<span style="color:red">' + value + '</span>';
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ def get_columns():
|
|||||||
{
|
{
|
||||||
"fieldname": "fifo_queue_qty",
|
"fieldname": "fifo_queue_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": _("(C) Total qty in queue"),
|
"label": _("(C) Total Qty in Queue"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "fifo_qty_diff",
|
"fieldname": "fifo_qty_diff",
|
||||||
@@ -211,52 +211,52 @@ def get_columns():
|
|||||||
{
|
{
|
||||||
"fieldname": "stock_value_difference",
|
"fieldname": "stock_value_difference",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": _("(F) Stock Value Difference"),
|
"label": _("(F) Change in Stock Value"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "stock_value_from_diff",
|
"fieldname": "stock_value_from_diff",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": _("Balance Stock Value using (F)"),
|
"label": _("(G) Sum of Change in Stock Value"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "diff_value_diff",
|
"fieldname": "diff_value_diff",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": _("K - D"),
|
"label": _("G - D"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "fifo_stock_diff",
|
"fieldname": "fifo_stock_diff",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": _("(G) Stock Value difference (FIFO queue)"),
|
"label": _("(H) Change in Stock Value (FIFO Queue)"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "fifo_difference_diff",
|
"fieldname": "fifo_difference_diff",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": _("F - G"),
|
"label": _("H - F"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "valuation_rate",
|
"fieldname": "valuation_rate",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": _("(H) Valuation Rate"),
|
"label": _("(I) Valuation Rate"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "fifo_valuation_rate",
|
"fieldname": "fifo_valuation_rate",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": _("(I) Valuation Rate as per FIFO"),
|
"label": _("(J) Valuation Rate as per FIFO"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "fifo_valuation_diff",
|
"fieldname": "fifo_valuation_diff",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": _("H - I"),
|
"label": _("I - J"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "balance_value_by_qty",
|
"fieldname": "balance_value_by_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": _("(J) Valuation = Value (D) ÷ Qty (A)"),
|
"label": _("(K) Valuation = Value (D) ÷ Qty (A)"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "valuation_diff",
|
"fieldname": "valuation_diff",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": _("H - J"),
|
"label": _("I - K"),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
const DIFFERENCE_FIELD_NAMES = [
|
||||||
|
"difference_in_qty",
|
||||||
|
"fifo_qty_diff",
|
||||||
|
"fifo_value_diff",
|
||||||
|
"fifo_valuation_diff",
|
||||||
|
"valuation_diff",
|
||||||
|
"fifo_difference_diff",
|
||||||
|
"diff_value_diff"
|
||||||
|
];
|
||||||
|
|
||||||
|
frappe.query_reports["Stock Ledger Variance"] = {
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"fieldname": "item_code",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Item",
|
||||||
|
"options": "Item",
|
||||||
|
get_query: function() {
|
||||||
|
return {
|
||||||
|
filters: {is_stock_item: 1, has_serial_no: 0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "warehouse",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Warehouse",
|
||||||
|
"options": "Warehouse",
|
||||||
|
get_query: function() {
|
||||||
|
return {
|
||||||
|
filters: {is_group: 0, disabled: 0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "difference_in",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Difference In",
|
||||||
|
"options": [
|
||||||
|
"",
|
||||||
|
"Qty",
|
||||||
|
"Value",
|
||||||
|
"Valuation",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "include_disabled",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Include Disabled",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
formatter (value, row, column, data, default_formatter) {
|
||||||
|
value = default_formatter(value, row, column, data);
|
||||||
|
|
||||||
|
if (DIFFERENCE_FIELD_NAMES.includes(column.fieldname) && Math.abs(data[column.fieldname]) > 0.001) {
|
||||||
|
value = "<span style='color:red'>" + value + "</span>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
|
get_datatable_options(options) {
|
||||||
|
return Object.assign(options, {
|
||||||
|
checkboxColumn: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onload(report) {
|
||||||
|
report.page.add_inner_button(__('Create Reposting Entries'), () => {
|
||||||
|
let message = `
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Reposting Entries will change the value of
|
||||||
|
accounts Stock In Hand, and Stock Expenses
|
||||||
|
in the Trial Balance report and will also change
|
||||||
|
the Balance Value in the Stock Balance report.
|
||||||
|
</p>
|
||||||
|
<p>Are you sure you want to create Reposting Entries?</p>
|
||||||
|
</div>`;
|
||||||
|
let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows();
|
||||||
|
let selected_rows = indexes.map(i => frappe.query_report.data[i]);
|
||||||
|
|
||||||
|
if (!selected_rows.length) {
|
||||||
|
frappe.throw(__("Please select rows to create Reposting Entries"));
|
||||||
|
}
|
||||||
|
|
||||||
|
frappe.confirm(__(message), () => {
|
||||||
|
frappe.call({
|
||||||
|
method: 'erpnext.stock.report.stock_ledger_invariant_check.stock_ledger_invariant_check.create_reposting_entries',
|
||||||
|
args: {
|
||||||
|
rows: selected_rows,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"add_total_row": 0,
|
||||||
|
"columns": [],
|
||||||
|
"creation": "2023-09-20 10:44:19.414449",
|
||||||
|
"disabled": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Report",
|
||||||
|
"filters": [],
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": "Yes",
|
||||||
|
"letterhead": null,
|
||||||
|
"modified": "2023-09-20 10:44:19.414449",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Stock",
|
||||||
|
"name": "Stock Ledger Variance",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"prepared_report": 0,
|
||||||
|
"ref_doctype": "Stock Ledger Entry",
|
||||||
|
"report_name": "Stock Ledger Variance",
|
||||||
|
"report_type": "Script Report",
|
||||||
|
"roles": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,279 @@
|
|||||||
|
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import cint, flt
|
||||||
|
|
||||||
|
from erpnext.stock.report.stock_ledger_invariant_check.stock_ledger_invariant_check import (
|
||||||
|
get_data as stock_ledger_invariant_check,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def execute(filters=None):
|
||||||
|
columns, data = [], []
|
||||||
|
|
||||||
|
filters = frappe._dict(filters or {})
|
||||||
|
columns = get_columns()
|
||||||
|
data = get_data(filters)
|
||||||
|
|
||||||
|
return columns, data
|
||||||
|
|
||||||
|
|
||||||
|
def get_columns():
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"fieldname": "name",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": _("Stock Ledger Entry"),
|
||||||
|
"options": "Stock Ledger Entry",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "posting_date",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": _("Posting Date"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "posting_time",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": _("Posting Time"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "creation",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": _("Creation"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "item_code",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": _("Item"),
|
||||||
|
"options": "Item",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "warehouse",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": _("Warehouse"),
|
||||||
|
"options": "Warehouse",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "voucher_type",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": _("Voucher Type"),
|
||||||
|
"options": "DocType",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "voucher_no",
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"label": _("Voucher No"),
|
||||||
|
"options": "voucher_type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "batch_no",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": _("Batch"),
|
||||||
|
"options": "Batch",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "use_batchwise_valuation",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": _("Batchwise Valuation"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "actual_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("Qty Change"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "incoming_rate",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("Incoming Rate"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "consumption_rate",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("Consumption Rate"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "qty_after_transaction",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("(A) Qty After Transaction"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "expected_qty_after_transaction",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("(B) Expected Qty After Transaction"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "difference_in_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("A - B"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "stock_queue",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": _("FIFO/LIFO Queue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "fifo_queue_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("(C) Total Qty in Queue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "fifo_qty_diff",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("A - C"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "stock_value",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("(D) Balance Stock Value"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "fifo_stock_value",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("(E) Balance Stock Value in Queue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "fifo_value_diff",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("D - E"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "stock_value_difference",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("(F) Change in Stock Value"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "stock_value_from_diff",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("(G) Sum of Change in Stock Value"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "diff_value_diff",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("G - D"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "fifo_stock_diff",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("(H) Change in Stock Value (FIFO Queue)"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "fifo_difference_diff",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("H - F"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "valuation_rate",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("(I) Valuation Rate"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "fifo_valuation_rate",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("(J) Valuation Rate as per FIFO"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "fifo_valuation_diff",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("I - J"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "balance_value_by_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("(K) Valuation = Value (D) ÷ Qty (A)"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "valuation_diff",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": _("I - K"),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_data(filters=None):
|
||||||
|
filters = frappe._dict(filters or {})
|
||||||
|
item_warehouse_map = get_item_warehouse_combinations(filters)
|
||||||
|
|
||||||
|
data = []
|
||||||
|
if item_warehouse_map:
|
||||||
|
precision = cint(frappe.db.get_single_value("System Settings", "float_precision"))
|
||||||
|
|
||||||
|
for item_warehouse in item_warehouse_map:
|
||||||
|
report_data = stock_ledger_invariant_check(item_warehouse)
|
||||||
|
|
||||||
|
if not report_data:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for row in report_data:
|
||||||
|
if has_difference(row, precision, filters.difference_in):
|
||||||
|
data.append(add_item_warehouse_details(row, item_warehouse))
|
||||||
|
break
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def get_item_warehouse_combinations(filters: dict = None) -> dict:
|
||||||
|
filters = frappe._dict(filters or {})
|
||||||
|
|
||||||
|
bin = frappe.qb.DocType("Bin")
|
||||||
|
item = frappe.qb.DocType("Item")
|
||||||
|
warehouse = frappe.qb.DocType("Warehouse")
|
||||||
|
|
||||||
|
query = (
|
||||||
|
frappe.qb.from_(bin)
|
||||||
|
.inner_join(item)
|
||||||
|
.on(bin.item_code == item.name)
|
||||||
|
.inner_join(warehouse)
|
||||||
|
.on(bin.warehouse == warehouse.name)
|
||||||
|
.select(
|
||||||
|
bin.item_code,
|
||||||
|
bin.warehouse,
|
||||||
|
)
|
||||||
|
.where((item.is_stock_item == 1) & (item.has_serial_no == 0) & (warehouse.is_group == 0))
|
||||||
|
)
|
||||||
|
|
||||||
|
if filters.item_code:
|
||||||
|
query = query.where(item.name == filters.item_code)
|
||||||
|
if filters.warehouse:
|
||||||
|
query = query.where(warehouse.name == filters.warehouse)
|
||||||
|
if not filters.include_disabled:
|
||||||
|
query = query.where((item.disabled == 0) & (warehouse.disabled == 0))
|
||||||
|
|
||||||
|
return query.run(as_dict=1)
|
||||||
|
|
||||||
|
|
||||||
|
def has_difference(row, precision, difference_in):
|
||||||
|
has_qty_difference = flt(row.difference_in_qty, precision) or flt(row.fifo_qty_diff, precision)
|
||||||
|
has_value_difference = (
|
||||||
|
flt(row.diff_value_diff, precision)
|
||||||
|
or flt(row.fifo_value_diff, precision)
|
||||||
|
or flt(row.fifo_difference_diff, precision)
|
||||||
|
)
|
||||||
|
has_valuation_difference = flt(row.valuation_diff, precision) or flt(
|
||||||
|
row.fifo_valuation_diff, precision
|
||||||
|
)
|
||||||
|
|
||||||
|
if difference_in == "Qty" and has_qty_difference:
|
||||||
|
return True
|
||||||
|
elif difference_in == "Value" and has_value_difference:
|
||||||
|
return True
|
||||||
|
elif difference_in == "Valuation" and has_valuation_difference:
|
||||||
|
return True
|
||||||
|
elif difference_in not in ["Qty", "Value", "Valuation"] and (
|
||||||
|
has_qty_difference or has_value_difference or has_valuation_difference
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def add_item_warehouse_details(row, item_warehouse):
|
||||||
|
row.update(
|
||||||
|
{
|
||||||
|
"item_code": item_warehouse.item_code,
|
||||||
|
"warehouse": item_warehouse.warehouse,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return row
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
<span class="in-green has-stock">
|
<span class="in-green has-stock">
|
||||||
{{ _('In stock') }}
|
{{ _('In stock') }}
|
||||||
{% if product_info.show_stock_qty and product_info.stock_qty %}
|
{% if product_info.show_stock_qty and product_info.stock_qty %}
|
||||||
({{ product_info.stock_qty[0][0] }})
|
({{ product_info.stock_qty }})
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -25,9 +25,19 @@ def get_context(context):
|
|||||||
|
|
||||||
|
|
||||||
def get_stock_availability(item_code, warehouse):
|
def get_stock_availability(item_code, warehouse):
|
||||||
stock_qty = frappe.utils.flt(
|
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
|
||||||
frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "actual_qty")
|
|
||||||
)
|
if warehouse and frappe.get_cached_value("Warehouse", warehouse, "is_group") == 1:
|
||||||
|
warehouses = get_child_warehouses(warehouse)
|
||||||
|
else:
|
||||||
|
warehouses = [warehouse] if warehouse else []
|
||||||
|
|
||||||
|
stock_qty = 0.0
|
||||||
|
for warehouse in warehouses:
|
||||||
|
stock_qty += frappe.utils.flt(
|
||||||
|
frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, "actual_qty")
|
||||||
|
)
|
||||||
|
|
||||||
return bool(stock_qty)
|
return bool(stock_qty)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1312,7 +1312,7 @@ Invalid GSTIN! A GSTIN must have 15 characters.,Ungültige GSTIN! Eine GSTIN mus
|
|||||||
Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.,Ungültige GSTIN! Die ersten beiden Ziffern von GSTIN sollten mit der Statusnummer {0} übereinstimmen.,
|
Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.,Ungültige GSTIN! Die ersten beiden Ziffern von GSTIN sollten mit der Statusnummer {0} übereinstimmen.,
|
||||||
Invalid GSTIN! The input you've entered doesn't match the format of GSTIN.,Ungültige GSTIN! Die von Ihnen eingegebene Eingabe stimmt nicht mit dem Format von GSTIN überein.,
|
Invalid GSTIN! The input you've entered doesn't match the format of GSTIN.,Ungültige GSTIN! Die von Ihnen eingegebene Eingabe stimmt nicht mit dem Format von GSTIN überein.,
|
||||||
Invalid Posting Time,Ungültige Buchungszeit,
|
Invalid Posting Time,Ungültige Buchungszeit,
|
||||||
Invalid Purchase Invoice,Ungültige Einkaufsrechnung,
|
Invalid Purchase Invoice,Ungültige Eingangsrechnung,
|
||||||
Invalid attribute {0} {1},Ungültiges Attribut {0} {1},
|
Invalid attribute {0} {1},Ungültiges Attribut {0} {1},
|
||||||
Invalid quantity specified for item {0}. Quantity should be greater than 0.,Ungültzige Anzahl für Artikel {0} angegeben. Anzahl sollte größer als 0 sein.,
|
Invalid quantity specified for item {0}. Quantity should be greater than 0.,Ungültzige Anzahl für Artikel {0} angegeben. Anzahl sollte größer als 0 sein.,
|
||||||
Invalid reference {0} {1},Ungültige Referenz {0} {1},
|
Invalid reference {0} {1},Ungültige Referenz {0} {1},
|
||||||
@@ -1970,7 +1970,7 @@ Please click on 'Generate Schedule',"Bitte auf ""Zeitplan generieren"" klicken",
|
|||||||
Please click on 'Generate Schedule' to fetch Serial No added for Item {0},"Bitte auf ""Zeitplan generieren"" klicken, um die Seriennummer für Artikel {0} abzurufen",
|
Please click on 'Generate Schedule' to fetch Serial No added for Item {0},"Bitte auf ""Zeitplan generieren"" klicken, um die Seriennummer für Artikel {0} abzurufen",
|
||||||
Please click on 'Generate Schedule' to get schedule,"Bitte auf ""Zeitplan generieren"" klicken, um den Zeitplan zu erhalten",
|
Please click on 'Generate Schedule' to get schedule,"Bitte auf ""Zeitplan generieren"" klicken, um den Zeitplan zu erhalten",
|
||||||
Please confirm once you have completed your training,"Bitte bestätigen Sie, sobald Sie Ihre Ausbildung abgeschlossen haben",
|
Please confirm once you have completed your training,"Bitte bestätigen Sie, sobald Sie Ihre Ausbildung abgeschlossen haben",
|
||||||
Please create purchase receipt or purchase invoice for the item {0},Bitte erstellen Sie eine Kaufquittung oder eine Kaufrechnung für den Artikel {0},
|
Please create purchase receipt or purchase invoice for the item {0},Bitte erstellen Sie eine Kaufquittung oder eine Eingangsrechnungen für den Artikel {0},
|
||||||
Please define grade for Threshold 0%,Bitte definieren Sie Grade for Threshold 0%,
|
Please define grade for Threshold 0%,Bitte definieren Sie Grade for Threshold 0%,
|
||||||
Please enable Applicable on Booking Actual Expenses,Bitte aktivieren Sie Anwendbar bei der Buchung von tatsächlichen Ausgaben,
|
Please enable Applicable on Booking Actual Expenses,Bitte aktivieren Sie Anwendbar bei der Buchung von tatsächlichen Ausgaben,
|
||||||
Please enable Applicable on Purchase Order and Applicable on Booking Actual Expenses,Bitte aktivieren Sie Anwendbar bei Bestellung und Anwendbar bei Buchung von tatsächlichen Ausgaben,
|
Please enable Applicable on Purchase Order and Applicable on Booking Actual Expenses,Bitte aktivieren Sie Anwendbar bei Bestellung und Anwendbar bei Buchung von tatsächlichen Ausgaben,
|
||||||
@@ -4937,7 +4937,7 @@ POS Customer Group,POS Kundengruppe,
|
|||||||
POS Field,POS-Feld,
|
POS Field,POS-Feld,
|
||||||
POS Item Group,POS Artikelgruppe,
|
POS Item Group,POS Artikelgruppe,
|
||||||
Company Address,Anschrift des Unternehmens,
|
Company Address,Anschrift des Unternehmens,
|
||||||
Update Stock,Lagerbestand aktualisieren,
|
Update Stock,Lagerbestand aktualisieren,
|
||||||
Ignore Pricing Rule,Preisregel ignorieren,
|
Ignore Pricing Rule,Preisregel ignorieren,
|
||||||
Applicable for Users,Anwendbar für Benutzer,
|
Applicable for Users,Anwendbar für Benutzer,
|
||||||
Sales Invoice Payment,Ausgangsrechnung-Zahlungen,
|
Sales Invoice Payment,Ausgangsrechnung-Zahlungen,
|
||||||
@@ -5134,7 +5134,6 @@ ACC-SINV-.YYYY.-,ACC-SINV-.JJJJ.-,
|
|||||||
Include Payment (POS),(POS) Zahlung einschließen,
|
Include Payment (POS),(POS) Zahlung einschließen,
|
||||||
Offline POS Name,Offline-Verkaufsstellen-Name,
|
Offline POS Name,Offline-Verkaufsstellen-Name,
|
||||||
Is Return (Credit Note),ist Rücklieferung (Gutschrift),
|
Is Return (Credit Note),ist Rücklieferung (Gutschrift),
|
||||||
Return Against Sales Invoice,Zurück zur Kundenrechnung,
|
|
||||||
Update Billed Amount in Sales Order,Aktualisierung des Rechnungsbetrags im Auftrag,
|
Update Billed Amount in Sales Order,Aktualisierung des Rechnungsbetrags im Auftrag,
|
||||||
Customer PO Details,Auftragsdetails,
|
Customer PO Details,Auftragsdetails,
|
||||||
Customer's Purchase Order,Bestellung des Kunden,
|
Customer's Purchase Order,Bestellung des Kunden,
|
||||||
@@ -7673,8 +7672,8 @@ Default Company Bank Account,Standard-Bankkonto des Unternehmens,
|
|||||||
From Lead,Aus Lead,
|
From Lead,Aus Lead,
|
||||||
Account Manager,Kundenberater,
|
Account Manager,Kundenberater,
|
||||||
Accounts Manager,Buchhalter,
|
Accounts Manager,Buchhalter,
|
||||||
Allow Sales Invoice Creation Without Sales Order,Ermöglichen Sie die Erstellung von Kundenrechnungen ohne Auftrag,
|
Allow Sales Invoice Creation Without Sales Order,Ermöglichen Sie die Erstellung von Ausgangsrechnungen ohne Auftrag,
|
||||||
Allow Sales Invoice Creation Without Delivery Note,Ermöglichen Sie die Erstellung einer Ausgangsrechnung ohne Lieferschein,
|
Allow Sales Invoice Creation Without Delivery Note,Ermöglichen Sie die Erstellung von Ausgangsrechnungen ohne Lieferschein,
|
||||||
Default Price List,Standardpreisliste,
|
Default Price List,Standardpreisliste,
|
||||||
Primary Address and Contact,Hauptadresse und -kontakt,
|
Primary Address and Contact,Hauptadresse und -kontakt,
|
||||||
"Select, to make the customer searchable with these fields","Wählen Sie, um den Kunden mit diesen Feldern durchsuchbar zu machen",
|
"Select, to make the customer searchable with these fields","Wählen Sie, um den Kunden mit diesen Feldern durchsuchbar zu machen",
|
||||||
@@ -9598,8 +9597,8 @@ This role is allowed to submit transactions that exceed credit limits,"Diese Rol
|
|||||||
Show Inclusive Tax in Print,Inklusive Steuern im Druck anzeigen,
|
Show Inclusive Tax in Print,Inklusive Steuern im Druck anzeigen,
|
||||||
Only select this if you have set up the Cash Flow Mapper documents,"Wählen Sie diese Option nur, wenn Sie die Cash Flow Mapper-Dokumente eingerichtet haben",
|
Only select this if you have set up the Cash Flow Mapper documents,"Wählen Sie diese Option nur, wenn Sie die Cash Flow Mapper-Dokumente eingerichtet haben",
|
||||||
Payment Channel,Zahlungskanal,
|
Payment Channel,Zahlungskanal,
|
||||||
Is Purchase Order Required for Purchase Invoice & Receipt Creation?,Ist für die Erstellung von Kaufrechnungen und Quittungen eine Bestellung erforderlich?,
|
Is Purchase Order Required for Purchase Invoice & Receipt Creation?,Ist für die Erstellung von Eingangsrechnungen und Quittungen eine Bestellung erforderlich?,
|
||||||
Is Purchase Receipt Required for Purchase Invoice Creation?,Ist für die Erstellung der Kaufrechnung ein Kaufbeleg erforderlich?,
|
Is Purchase Receipt Required for Purchase Invoice Creation?,Ist für die Erstellung der Eingangsrechnungen ein Kaufbeleg erforderlich?,
|
||||||
Maintain Same Rate Throughout the Purchase Cycle,Behalten Sie den gleichen Preis während des gesamten Kaufzyklus bei,
|
Maintain Same Rate Throughout the Purchase Cycle,Behalten Sie den gleichen Preis während des gesamten Kaufzyklus bei,
|
||||||
Allow Item To Be Added Multiple Times in a Transaction,"Zulassen, dass ein Element in einer Transaktion mehrmals hinzugefügt wird",
|
Allow Item To Be Added Multiple Times in a Transaction,"Zulassen, dass ein Element in einer Transaktion mehrmals hinzugefügt wird",
|
||||||
Suppliers,Lieferanten,
|
Suppliers,Lieferanten,
|
||||||
@@ -9657,8 +9656,8 @@ Purchase Order already created for all Sales Order items,Bestellung bereits für
|
|||||||
Select Items,Gegenstände auswählen,
|
Select Items,Gegenstände auswählen,
|
||||||
Against Default Supplier,Gegen Standardlieferanten,
|
Against Default Supplier,Gegen Standardlieferanten,
|
||||||
Auto close Opportunity after the no. of days mentioned above,Gelegenheit zum automatischen Schließen nach der Nr. der oben genannten Tage,
|
Auto close Opportunity after the no. of days mentioned above,Gelegenheit zum automatischen Schließen nach der Nr. der oben genannten Tage,
|
||||||
Is Sales Order Required for Sales Invoice & Delivery Note Creation?,Ist ein Auftrag für die Erstellung von Kundenrechnungen und Lieferscheinen erforderlich?,
|
Is Sales Order Required for Sales Invoice & Delivery Note Creation?,Ist ein Auftrag für die Erstellung von Ausgangsrechnungen und Lieferscheinen erforderlich?,
|
||||||
Is Delivery Note Required for Sales Invoice Creation?,Ist für die Erstellung der Ausgangsrechnung ein Lieferschein erforderlich?,
|
Is Delivery Note Required for Sales Invoice Creation?,Ist ein Lieferschein für die Erstellung von Ausgangsrechnungen erforderlich?,
|
||||||
How often should Project and Company be updated based on Sales Transactions?,Wie oft sollten Projekt und Unternehmen basierend auf Verkaufstransaktionen aktualisiert werden?,
|
How often should Project and Company be updated based on Sales Transactions?,Wie oft sollten Projekt und Unternehmen basierend auf Verkaufstransaktionen aktualisiert werden?,
|
||||||
Allow User to Edit Price List Rate in Transactions,Benutzer darf Preisliste in Transaktionen bearbeiten,
|
Allow User to Edit Price List Rate in Transactions,Benutzer darf Preisliste in Transaktionen bearbeiten,
|
||||||
Allow Item to Be Added Multiple Times in a Transaction,"Zulassen, dass ein Element in einer Transaktion mehrmals hinzugefügt wird",
|
Allow Item to Be Added Multiple Times in a Transaction,"Zulassen, dass ein Element in einer Transaktion mehrmals hinzugefügt wird",
|
||||||
@@ -9804,7 +9803,7 @@ or it is not the default inventory account,oder es ist nicht das Standard-Invent
|
|||||||
Expense Head Changed,Ausgabenkopf geändert,
|
Expense Head Changed,Ausgabenkopf geändert,
|
||||||
because expense is booked against this account in Purchase Receipt {},weil die Kosten für dieses Konto im Kaufbeleg {} gebucht werden,
|
because expense is booked against this account in Purchase Receipt {},weil die Kosten für dieses Konto im Kaufbeleg {} gebucht werden,
|
||||||
as no Purchase Receipt is created against Item {}. ,da für Artikel {} kein Kaufbeleg erstellt wird.,
|
as no Purchase Receipt is created against Item {}. ,da für Artikel {} kein Kaufbeleg erstellt wird.,
|
||||||
This is done to handle accounting for cases when Purchase Receipt is created after Purchase Invoice,"Dies erfolgt zur Abrechnung von Fällen, in denen der Kaufbeleg nach der Kaufrechnung erstellt wird",
|
This is done to handle accounting for cases when Purchase Receipt is created after Purchase Invoice,"Dies erfolgt zur Abrechnung von Fällen, in denen der Kaufbeleg nach der Eingangsrechnung erstellt wird",
|
||||||
Purchase Order Required for item {},Bestellung erforderlich für Artikel {},
|
Purchase Order Required for item {},Bestellung erforderlich für Artikel {},
|
||||||
To submit the invoice without purchase order please set {} ,"Um die Rechnung ohne Bestellung einzureichen, setzen Sie bitte {}",
|
To submit the invoice without purchase order please set {} ,"Um die Rechnung ohne Bestellung einzureichen, setzen Sie bitte {}",
|
||||||
as {} in {},wie in {},
|
as {} in {},wie in {},
|
||||||
|
|||||||
|
Can't render this file because it is too large.
|
@@ -6,6 +6,7 @@ from frappe.utils import cint, flt, fmt_money, getdate, nowdate
|
|||||||
|
|
||||||
from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item
|
from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item
|
||||||
from erpnext.stock.doctype.batch.batch import get_batch_qty
|
from erpnext.stock.doctype.batch.batch import get_batch_qty
|
||||||
|
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
|
||||||
|
|
||||||
|
|
||||||
def get_web_item_qty_in_stock(item_code, item_warehouse_field, warehouse=None):
|
def get_web_item_qty_in_stock(item_code, item_warehouse_field, warehouse=None):
|
||||||
@@ -22,23 +23,31 @@ def get_web_item_qty_in_stock(item_code, item_warehouse_field, warehouse=None):
|
|||||||
"Website Item", {"item_code": template_item_code}, item_warehouse_field
|
"Website Item", {"item_code": template_item_code}, item_warehouse_field
|
||||||
)
|
)
|
||||||
|
|
||||||
if warehouse:
|
if warehouse and frappe.get_cached_value("Warehouse", warehouse, "is_group") == 1:
|
||||||
stock_qty = frappe.db.sql(
|
warehouses = get_child_warehouses(warehouse)
|
||||||
"""
|
else:
|
||||||
select GREATEST(S.actual_qty - S.reserved_qty - S.reserved_qty_for_production - S.reserved_qty_for_sub_contract, 0) / IFNULL(C.conversion_factor, 1)
|
warehouses = [warehouse] if warehouse else []
|
||||||
from tabBin S
|
|
||||||
inner join `tabItem` I on S.item_code = I.Item_code
|
|
||||||
left join `tabUOM Conversion Detail` C on I.sales_uom = C.uom and C.parent = I.Item_code
|
|
||||||
where S.item_code=%s and S.warehouse=%s""",
|
|
||||||
(item_code, warehouse),
|
|
||||||
)
|
|
||||||
|
|
||||||
if stock_qty:
|
total_stock = 0.0
|
||||||
stock_qty = adjust_qty_for_expired_items(item_code, stock_qty, warehouse)
|
if warehouses:
|
||||||
in_stock = stock_qty[0][0] > 0 and 1 or 0
|
for warehouse in warehouses:
|
||||||
|
stock_qty = frappe.db.sql(
|
||||||
|
"""
|
||||||
|
select GREATEST(S.actual_qty - S.reserved_qty - S.reserved_qty_for_production - S.reserved_qty_for_sub_contract, 0) / IFNULL(C.conversion_factor, 1)
|
||||||
|
from tabBin S
|
||||||
|
inner join `tabItem` I on S.item_code = I.Item_code
|
||||||
|
left join `tabUOM Conversion Detail` C on I.sales_uom = C.uom and C.parent = I.Item_code
|
||||||
|
where S.item_code=%s and S.warehouse=%s""",
|
||||||
|
(item_code, warehouse),
|
||||||
|
)
|
||||||
|
|
||||||
|
if stock_qty:
|
||||||
|
total_stock += adjust_qty_for_expired_items(item_code, stock_qty, warehouse)
|
||||||
|
|
||||||
|
in_stock = total_stock > 0 and 1 or 0
|
||||||
|
|
||||||
return frappe._dict(
|
return frappe._dict(
|
||||||
{"in_stock": in_stock, "stock_qty": stock_qty, "is_stock_item": is_stock_item}
|
{"in_stock": in_stock, "stock_qty": total_stock, "is_stock_item": is_stock_item}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -56,7 +65,7 @@ def adjust_qty_for_expired_items(item_code, stock_qty, warehouse):
|
|||||||
if not stock_qty[0][0]:
|
if not stock_qty[0][0]:
|
||||||
break
|
break
|
||||||
|
|
||||||
return stock_qty
|
return stock_qty[0][0] if stock_qty else 0
|
||||||
|
|
||||||
|
|
||||||
def get_expired_batches(batches):
|
def get_expired_batches(batches):
|
||||||
|
|||||||
Reference in New Issue
Block a user