mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-24 07:29:22 +00:00
chore: release v14 (#33452)
* fix: typerror on multi warehouse in Packed Items DN(with bundled item with varying warehouses)-> Sales Invoice. (cherry picked from commite684eb32d0) * test: type error on bundled products with different warehouses (cherry picked from commit5918bb03f7) * fix: payment terms and sales partner filter issue in AR/AP report (cherry picked from commit13c4420f42) * fix: timeout error while submitting stock entry Co-authored-by: Ankush Menat <ankush@frappe.io> (cherry picked from commita05c47e499) * fix: `shipping_address` in PO (cherry picked from commit7e1b6b3c2a) * refactor: Customer and Supplier Ledger summary will have hidden fields for better handling of user permission (#33433) * feat: Accounting Dimension updation in Payment Request and Entry (#33411) * fix: `shipping_address` for non-drop shipping item (cherry picked from commit67a7ccf3ce) * fix: Random behaviour while picking items using picklist (backport #33449) (#33450) fix: Random behaviour while picking items using picklist (#33449) (cherry picked from commit8263bf9a9a) Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com> * fix: Multiple rows for same warehouse and batches in pick list (backport #33456) (#33458) fix: Multiple rows for same warehouse and batches in pick list (#33456) (cherry picked from commitd2686ce75b) Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com> * fix: Default dimensions on fetching items from BOM (backport #33439) (#33459) fix: Default dimensions on fetching items from BOM (#33439) (cherry picked from commit0b75aa5390) Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com> Co-authored-by: ruthra kumar <ruthra@erpnext.com> Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com> Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
This commit is contained in:
@@ -1758,6 +1758,8 @@ def get_payment_entry(
|
|||||||
pe.setup_party_account_field()
|
pe.setup_party_account_field()
|
||||||
pe.set_missing_values()
|
pe.set_missing_values()
|
||||||
|
|
||||||
|
update_accounting_dimensions(pe, doc)
|
||||||
|
|
||||||
if party_account and bank:
|
if party_account and bank:
|
||||||
pe.set_exchange_rate(ref_doc=reference_doc)
|
pe.set_exchange_rate(ref_doc=reference_doc)
|
||||||
pe.set_amounts()
|
pe.set_amounts()
|
||||||
@@ -1775,6 +1777,18 @@ def get_payment_entry(
|
|||||||
return pe
|
return pe
|
||||||
|
|
||||||
|
|
||||||
|
def update_accounting_dimensions(pe, doc):
|
||||||
|
"""
|
||||||
|
Updates accounting dimensions in Payment Entry based on the accounting dimensions in the reference document
|
||||||
|
"""
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||||
|
get_accounting_dimensions,
|
||||||
|
)
|
||||||
|
|
||||||
|
for dimension in get_accounting_dimensions():
|
||||||
|
pe.set(dimension, doc.get(dimension))
|
||||||
|
|
||||||
|
|
||||||
def get_bank_cash_account(doc, bank_account):
|
def get_bank_cash_account(doc, bank_account):
|
||||||
bank = get_default_bank_cash_account(
|
bank = get_default_bank_cash_account(
|
||||||
doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"), account=bank_account
|
doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"), account=bank_account
|
||||||
|
|||||||
@@ -32,6 +32,10 @@
|
|||||||
"iban",
|
"iban",
|
||||||
"branch_code",
|
"branch_code",
|
||||||
"swift_number",
|
"swift_number",
|
||||||
|
"accounting_dimensions_section",
|
||||||
|
"cost_center",
|
||||||
|
"dimension_col_break",
|
||||||
|
"project",
|
||||||
"recipient_and_message",
|
"recipient_and_message",
|
||||||
"print_format",
|
"print_format",
|
||||||
"email_to",
|
"email_to",
|
||||||
@@ -362,13 +366,35 @@
|
|||||||
"label": "Payment Channel",
|
"label": "Payment Channel",
|
||||||
"options": "\nEmail\nPhone",
|
"options": "\nEmail\nPhone",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsible": 1,
|
||||||
|
"fieldname": "accounting_dimensions_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Accounting Dimensions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "cost_center",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Cost Center",
|
||||||
|
"options": "Cost Center"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "dimension_col_break",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "project",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Project",
|
||||||
|
"options": "Project"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"in_create": 1,
|
"in_create": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-09-30 16:19:43.680025",
|
"modified": "2022-12-21 16:56:40.115737",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Request",
|
"name": "Payment Request",
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ from frappe.utils import flt, get_url, nowdate
|
|||||||
from frappe.utils.background_jobs import enqueue
|
from frappe.utils.background_jobs import enqueue
|
||||||
from payments.utils import get_payment_gateway_controller
|
from payments.utils import get_payment_gateway_controller
|
||||||
|
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||||
|
get_accounting_dimensions,
|
||||||
|
)
|
||||||
from erpnext.accounts.doctype.payment_entry.payment_entry import (
|
from erpnext.accounts.doctype.payment_entry.payment_entry import (
|
||||||
get_company_defaults,
|
get_company_defaults,
|
||||||
get_payment_entry,
|
get_payment_entry,
|
||||||
@@ -263,6 +266,17 @@ class PaymentRequest(Document):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Update dimensions
|
||||||
|
payment_entry.update(
|
||||||
|
{
|
||||||
|
"cost_center": self.get("cost_center"),
|
||||||
|
"project": self.get("project"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
for dimension in get_accounting_dimensions():
|
||||||
|
payment_entry.update({dimension: self.get(dimension)})
|
||||||
|
|
||||||
if payment_entry.difference_amount:
|
if payment_entry.difference_amount:
|
||||||
company_details = get_company_defaults(ref_doc.company)
|
company_details = get_company_defaults(ref_doc.company)
|
||||||
|
|
||||||
@@ -442,6 +456,17 @@ def make_payment_request(**args):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Update dimensions
|
||||||
|
pr.update(
|
||||||
|
{
|
||||||
|
"cost_center": ref_doc.get("cost_center"),
|
||||||
|
"project": ref_doc.get("project"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
for dimension in get_accounting_dimensions():
|
||||||
|
pr.update({dimension: ref_doc.get(dimension)})
|
||||||
|
|
||||||
if args.order_type == "Shopping Cart" or args.mute_email:
|
if args.order_type == "Shopping Cart" or args.mute_email:
|
||||||
pr.flags.mute_email = True
|
pr.flags.mute_email = True
|
||||||
|
|
||||||
|
|||||||
@@ -794,19 +794,19 @@ class ReceivablePayableReport(object):
|
|||||||
|
|
||||||
if self.filters.get("payment_terms_template"):
|
if self.filters.get("payment_terms_template"):
|
||||||
self.qb_selection_filter.append(
|
self.qb_selection_filter.append(
|
||||||
self.ple.party_isin(
|
self.ple.party.isin(
|
||||||
qb.from_(self.customer).where(
|
qb.from_(self.customer)
|
||||||
self.customer.payment_terms == self.filters.get("payment_terms_template")
|
.select(self.customer.name)
|
||||||
)
|
.where(self.customer.payment_terms == self.filters.get("payment_terms_template"))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.filters.get("sales_partner"):
|
if self.filters.get("sales_partner"):
|
||||||
self.qb_selection_filter.append(
|
self.qb_selection_filter.append(
|
||||||
self.ple.party_isin(
|
self.ple.party.isin(
|
||||||
qb.from_(self.customer).where(
|
qb.from_(self.customer)
|
||||||
self.customer.default_sales_partner == self.filters.get("payment_terms_template")
|
.select(self.customer.name)
|
||||||
)
|
.where(self.customer.default_sales_partner == self.filters.get("payment_terms_template"))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class PartyLedgerSummaryReport(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.get_gl_entries()
|
self.get_gl_entries()
|
||||||
|
self.get_additional_columns()
|
||||||
self.get_return_invoices()
|
self.get_return_invoices()
|
||||||
self.get_party_adjustment_amounts()
|
self.get_party_adjustment_amounts()
|
||||||
|
|
||||||
@@ -33,6 +34,42 @@ class PartyLedgerSummaryReport(object):
|
|||||||
data = self.get_data()
|
data = self.get_data()
|
||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
|
def get_additional_columns(self):
|
||||||
|
"""
|
||||||
|
Additional Columns for 'User Permission' based access control
|
||||||
|
"""
|
||||||
|
from frappe import qb
|
||||||
|
|
||||||
|
if self.filters.party_type == "Customer":
|
||||||
|
self.territories = frappe._dict({})
|
||||||
|
self.customer_group = frappe._dict({})
|
||||||
|
|
||||||
|
customer = qb.DocType("Customer")
|
||||||
|
result = (
|
||||||
|
frappe.qb.from_(customer)
|
||||||
|
.select(
|
||||||
|
customer.name, customer.territory, customer.customer_group, customer.default_sales_partner
|
||||||
|
)
|
||||||
|
.where((customer.disabled == 0))
|
||||||
|
.run(as_dict=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
for x in result:
|
||||||
|
self.territories[x.name] = x.territory
|
||||||
|
self.customer_group[x.name] = x.customer_group
|
||||||
|
else:
|
||||||
|
self.supplier_group = frappe._dict({})
|
||||||
|
supplier = qb.DocType("Supplier")
|
||||||
|
result = (
|
||||||
|
frappe.qb.from_(supplier)
|
||||||
|
.select(supplier.name, supplier.supplier_group)
|
||||||
|
.where((supplier.disabled == 0))
|
||||||
|
.run(as_dict=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
for x in result:
|
||||||
|
self.supplier_group[x.name] = x.supplier_group
|
||||||
|
|
||||||
def get_columns(self):
|
def get_columns(self):
|
||||||
columns = [
|
columns = [
|
||||||
{
|
{
|
||||||
@@ -116,6 +153,35 @@ class PartyLedgerSummaryReport(object):
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Hidden columns for handling 'User Permissions'
|
||||||
|
if self.filters.party_type == "Customer":
|
||||||
|
columns += [
|
||||||
|
{
|
||||||
|
"label": _("Territory"),
|
||||||
|
"fieldname": "territory",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Territory",
|
||||||
|
"hidden": 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Customer Group"),
|
||||||
|
"fieldname": "customer_group",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Customer Group",
|
||||||
|
"hidden": 1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
columns += [
|
||||||
|
{
|
||||||
|
"label": _("Supplier Group"),
|
||||||
|
"fieldname": "supplier_group",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Supplier Group",
|
||||||
|
"hidden": 1,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
@@ -143,6 +209,12 @@ class PartyLedgerSummaryReport(object):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.filters.party_type == "Customer":
|
||||||
|
self.party_data[gle.party].update({"territory": self.territories.get(gle.party)})
|
||||||
|
self.party_data[gle.party].update({"customer_group": self.customer_group.get(gle.party)})
|
||||||
|
else:
|
||||||
|
self.party_data[gle.party].update({"supplier_group": self.supplier_group.get(gle.party)})
|
||||||
|
|
||||||
amount = gle.get(invoice_dr_or_cr) - gle.get(reverse_dr_or_cr)
|
amount = gle.get(invoice_dr_or_cr) - gle.get(reverse_dr_or_cr)
|
||||||
self.party_data[gle.party].closing_balance += amount
|
self.party_data[gle.party].closing_balance += amount
|
||||||
|
|
||||||
|
|||||||
@@ -607,6 +607,7 @@ class GrossProfitGenerator(object):
|
|||||||
return abs(previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
|
return abs(previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
|
||||||
else:
|
else:
|
||||||
return flt(row.qty) * self.get_average_buying_rate(row, item_code)
|
return flt(row.qty) * self.get_average_buying_rate(row, item_code)
|
||||||
|
return 0.0
|
||||||
|
|
||||||
def get_buying_amount(self, row, item_code):
|
def get_buying_amount(self, row, item_code):
|
||||||
# IMP NOTE
|
# IMP NOTE
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ from frappe.utils import add_days, flt, nowdate
|
|||||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_delivery_note
|
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_delivery_note
|
||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
from erpnext.accounts.report.gross_profit.gross_profit import execute
|
from erpnext.accounts.report.gross_profit.gross_profit import execute
|
||||||
|
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
|
||||||
|
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||||
from erpnext.stock.doctype.item.test_item import create_item
|
from erpnext.stock.doctype.item.test_item import create_item
|
||||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||||
|
|
||||||
@@ -14,6 +16,7 @@ class TestGrossProfit(FrappeTestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.create_company()
|
self.create_company()
|
||||||
self.create_item()
|
self.create_item()
|
||||||
|
self.create_bundle()
|
||||||
self.create_customer()
|
self.create_customer()
|
||||||
self.create_sales_invoice()
|
self.create_sales_invoice()
|
||||||
self.clear_old_entries()
|
self.clear_old_entries()
|
||||||
@@ -42,6 +45,7 @@ class TestGrossProfit(FrappeTestCase):
|
|||||||
self.company = company.name
|
self.company = company.name
|
||||||
self.cost_center = company.cost_center
|
self.cost_center = company.cost_center
|
||||||
self.warehouse = "Stores - " + abbr
|
self.warehouse = "Stores - " + abbr
|
||||||
|
self.finished_warehouse = "Finished Goods - " + abbr
|
||||||
self.income_account = "Sales - " + abbr
|
self.income_account = "Sales - " + abbr
|
||||||
self.expense_account = "Cost of Goods Sold - " + abbr
|
self.expense_account = "Cost of Goods Sold - " + abbr
|
||||||
self.debit_to = "Debtors - " + abbr
|
self.debit_to = "Debtors - " + abbr
|
||||||
@@ -53,6 +57,23 @@ class TestGrossProfit(FrappeTestCase):
|
|||||||
)
|
)
|
||||||
self.item = item if isinstance(item, str) else item.item_code
|
self.item = item if isinstance(item, str) else item.item_code
|
||||||
|
|
||||||
|
def create_bundle(self):
|
||||||
|
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
||||||
|
|
||||||
|
item2 = create_item(
|
||||||
|
item_code="_Test GP Item 2", is_stock_item=1, company=self.company, warehouse=self.warehouse
|
||||||
|
)
|
||||||
|
self.item2 = item2 if isinstance(item2, str) else item2.item_code
|
||||||
|
|
||||||
|
# This will be parent item
|
||||||
|
bundle = create_item(
|
||||||
|
item_code="_Test GP bundle", is_stock_item=0, company=self.company, warehouse=self.warehouse
|
||||||
|
)
|
||||||
|
self.bundle = bundle if isinstance(bundle, str) else bundle.item_code
|
||||||
|
|
||||||
|
# Create Product Bundle
|
||||||
|
self.product_bundle = make_product_bundle(parent=self.bundle, items=[self.item, self.item2])
|
||||||
|
|
||||||
def create_customer(self):
|
def create_customer(self):
|
||||||
name = "_Test GP Customer"
|
name = "_Test GP Customer"
|
||||||
if frappe.db.exists("Customer", name):
|
if frappe.db.exists("Customer", name):
|
||||||
@@ -93,6 +114,28 @@ class TestGrossProfit(FrappeTestCase):
|
|||||||
)
|
)
|
||||||
return sinv
|
return sinv
|
||||||
|
|
||||||
|
def create_delivery_note(
|
||||||
|
self, item=None, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Helper function to populate default values in Delivery Note
|
||||||
|
"""
|
||||||
|
dnote = create_delivery_note(
|
||||||
|
company=self.company,
|
||||||
|
customer=self.customer,
|
||||||
|
currency="INR",
|
||||||
|
item=item or self.item,
|
||||||
|
qty=qty,
|
||||||
|
rate=rate,
|
||||||
|
cost_center=self.cost_center,
|
||||||
|
warehouse=self.warehouse,
|
||||||
|
return_against=None,
|
||||||
|
expense_account=self.expense_account,
|
||||||
|
do_not_save=do_not_save,
|
||||||
|
do_not_submit=do_not_submit,
|
||||||
|
)
|
||||||
|
return dnote
|
||||||
|
|
||||||
def clear_old_entries(self):
|
def clear_old_entries(self):
|
||||||
doctype_list = [
|
doctype_list = [
|
||||||
"Sales Invoice",
|
"Sales Invoice",
|
||||||
@@ -207,3 +250,55 @@ class TestGrossProfit(FrappeTestCase):
|
|||||||
}
|
}
|
||||||
gp_entry = [x for x in data if x.parent_invoice == sinv.name]
|
gp_entry = [x for x in data if x.parent_invoice == sinv.name]
|
||||||
self.assertDictContainsSubset(expected_entry_with_dn, gp_entry[0])
|
self.assertDictContainsSubset(expected_entry_with_dn, gp_entry[0])
|
||||||
|
|
||||||
|
def test_bundled_delivery_note_with_different_warehouses(self):
|
||||||
|
"""
|
||||||
|
Test Delivery Note with bundled item. Packed Item from the bundle having different warehouses
|
||||||
|
"""
|
||||||
|
se = make_stock_entry(
|
||||||
|
company=self.company,
|
||||||
|
item_code=self.item,
|
||||||
|
target=self.warehouse,
|
||||||
|
qty=1,
|
||||||
|
basic_rate=100,
|
||||||
|
do_not_submit=True,
|
||||||
|
)
|
||||||
|
item = se.items[0]
|
||||||
|
se.append(
|
||||||
|
"items",
|
||||||
|
{
|
||||||
|
"item_code": self.item2,
|
||||||
|
"s_warehouse": "",
|
||||||
|
"t_warehouse": self.finished_warehouse,
|
||||||
|
"qty": 1,
|
||||||
|
"basic_rate": 100,
|
||||||
|
"conversion_factor": item.conversion_factor or 1.0,
|
||||||
|
"transfer_qty": flt(item.qty) * (flt(item.conversion_factor) or 1.0),
|
||||||
|
"serial_no": item.serial_no,
|
||||||
|
"batch_no": item.batch_no,
|
||||||
|
"cost_center": item.cost_center,
|
||||||
|
"expense_account": item.expense_account,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
se = se.save().submit()
|
||||||
|
|
||||||
|
# Make a Delivery note with Product bundle
|
||||||
|
# Packed Items will have different warehouses
|
||||||
|
dnote = self.create_delivery_note(item=self.bundle, qty=1, rate=200, do_not_submit=True)
|
||||||
|
dnote.packed_items[1].warehouse = self.finished_warehouse
|
||||||
|
dnote = dnote.submit()
|
||||||
|
|
||||||
|
# make Sales Invoice for above delivery note
|
||||||
|
sinv = make_sales_invoice(dnote.name)
|
||||||
|
sinv = sinv.save().submit()
|
||||||
|
|
||||||
|
filters = frappe._dict(
|
||||||
|
company=self.company,
|
||||||
|
from_date=nowdate(),
|
||||||
|
to_date=nowdate(),
|
||||||
|
group_by="Invoice",
|
||||||
|
sales_invoice=sinv.name,
|
||||||
|
)
|
||||||
|
|
||||||
|
columns, data = execute(filters=filters)
|
||||||
|
self.assertGreater(len(data), 0)
|
||||||
|
|||||||
@@ -63,24 +63,6 @@ frappe.query_reports["Supplier Ledger Summary"] = {
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Payment Terms Template"
|
"options": "Payment Terms Template"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname":"territory",
|
|
||||||
"label": __("Territory"),
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"options": "Territory"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname":"sales_partner",
|
|
||||||
"label": __("Sales Partner"),
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"options": "Sales Partner"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname":"sales_person",
|
|
||||||
"label": __("Sales Person"),
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"options": "Sales Person"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname":"tax_id",
|
"fieldname":"tax_id",
|
||||||
"label": __("Tax Id"),
|
"label": __("Tax Id"),
|
||||||
|
|||||||
@@ -108,7 +108,7 @@
|
|||||||
"contact_display",
|
"contact_display",
|
||||||
"contact_mobile",
|
"contact_mobile",
|
||||||
"contact_email",
|
"contact_email",
|
||||||
"company_shipping_address_section",
|
"shipping_address_section",
|
||||||
"shipping_address",
|
"shipping_address",
|
||||||
"column_break_99",
|
"column_break_99",
|
||||||
"shipping_address_display",
|
"shipping_address_display",
|
||||||
@@ -385,7 +385,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "shipping_address",
|
"fieldname": "shipping_address",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Company Shipping Address",
|
"label": "Shipping Address",
|
||||||
"options": "Address",
|
"options": "Address",
|
||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
@@ -1207,11 +1207,6 @@
|
|||||||
"fieldtype": "Tab Break",
|
"fieldtype": "Tab Break",
|
||||||
"label": "Address & Contact"
|
"label": "Address & Contact"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "company_shipping_address_section",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"label": "Company Shipping Address"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "company_billing_address_section",
|
"fieldname": "company_billing_address_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
@@ -1263,13 +1258,18 @@
|
|||||||
"fieldname": "named_place",
|
"fieldname": "named_place",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Named Place"
|
"label": "Named Place"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "shipping_address_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Shipping Address"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 105,
|
"idx": 105,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-12-12 18:36:37.455134",
|
"modified": "2022-12-25 18:08:59.074182",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order",
|
"name": "Purchase Order",
|
||||||
|
|||||||
@@ -320,3 +320,4 @@ erpnext.patches.v13_0.update_schedule_type_in_loans
|
|||||||
erpnext.patches.v14_0.update_partial_tds_fields
|
erpnext.patches.v14_0.update_partial_tds_fields
|
||||||
erpnext.patches.v14_0.create_incoterms_and_migrate_shipment
|
erpnext.patches.v14_0.create_incoterms_and_migrate_shipment
|
||||||
erpnext.patches.v14_0.setup_clear_repost_logs
|
erpnext.patches.v14_0.setup_clear_repost_logs
|
||||||
|
erpnext.patches.v14_0.create_accounting_dimensions_for_payment_request
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import frappe
|
||||||
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
accounting_dimensions = frappe.db.get_all(
|
||||||
|
"Accounting Dimension", fields=["fieldname", "label", "document_type", "disabled"]
|
||||||
|
)
|
||||||
|
|
||||||
|
if not accounting_dimensions:
|
||||||
|
return
|
||||||
|
|
||||||
|
doctype = "Payment Request"
|
||||||
|
|
||||||
|
for d in accounting_dimensions:
|
||||||
|
field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname})
|
||||||
|
|
||||||
|
if field:
|
||||||
|
continue
|
||||||
|
|
||||||
|
df = {
|
||||||
|
"fieldname": d.fieldname,
|
||||||
|
"label": d.label,
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": d.document_type,
|
||||||
|
"insert_after": "accounting_dimensions_section",
|
||||||
|
}
|
||||||
|
|
||||||
|
create_custom_field(doctype, df, ignore_validate=True)
|
||||||
|
|
||||||
|
frappe.clear_cache(doctype=doctype)
|
||||||
@@ -1024,6 +1024,15 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None):
|
|||||||
]
|
]
|
||||||
items_to_map = list(set(items_to_map))
|
items_to_map = list(set(items_to_map))
|
||||||
|
|
||||||
|
def is_drop_ship_order(target):
|
||||||
|
drop_ship = True
|
||||||
|
for item in target.items:
|
||||||
|
if not item.delivered_by_supplier:
|
||||||
|
drop_ship = False
|
||||||
|
break
|
||||||
|
|
||||||
|
return drop_ship
|
||||||
|
|
||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
target.supplier = ""
|
target.supplier = ""
|
||||||
target.apply_discount_on = ""
|
target.apply_discount_on = ""
|
||||||
@@ -1031,8 +1040,14 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None):
|
|||||||
target.discount_amount = 0.0
|
target.discount_amount = 0.0
|
||||||
target.inter_company_order_reference = ""
|
target.inter_company_order_reference = ""
|
||||||
target.shipping_rule = ""
|
target.shipping_rule = ""
|
||||||
target.customer = ""
|
|
||||||
target.customer_name = ""
|
if is_drop_ship_order(target):
|
||||||
|
target.customer = source.customer
|
||||||
|
target.customer_name = source.customer_name
|
||||||
|
target.shipping_address = source.shipping_address_name
|
||||||
|
else:
|
||||||
|
target.customer = target.customer_name = target.shipping_address = None
|
||||||
|
|
||||||
target.run_method("set_missing_values")
|
target.run_method("set_missing_values")
|
||||||
target.run_method("calculate_taxes_and_totals")
|
target.run_method("calculate_taxes_and_totals")
|
||||||
|
|
||||||
|
|||||||
@@ -139,10 +139,11 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_tree": 1,
|
"is_tree": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-02-08 17:01:52.162202",
|
"modified": "2022-12-24 11:15:17.142746",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Setup",
|
"module": "Setup",
|
||||||
"name": "Customer Group",
|
"name": "Customer Group",
|
||||||
|
"naming_rule": "By fieldname",
|
||||||
"nsm_parent_field": "parent_customer_group",
|
"nsm_parent_field": "parent_customer_group",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
@@ -198,10 +199,19 @@
|
|||||||
"role": "Customer",
|
"role": "Customer",
|
||||||
"select": 1,
|
"select": 1,
|
||||||
"share": 1
|
"share": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Accounts User",
|
||||||
|
"share": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"search_fields": "parent_customer_group",
|
"search_fields": "parent_customer_group",
|
||||||
"show_name_in_global_search": 1,
|
"show_name_in_global_search": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC"
|
"sort_order": "DESC",
|
||||||
|
"states": []
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
"creation": "2013-01-10 16:34:24",
|
"creation": "2013-01-10 16:34:24",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Setup",
|
"document_type": "Setup",
|
||||||
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"supplier_group_name",
|
"supplier_group_name",
|
||||||
"parent_supplier_group",
|
"parent_supplier_group",
|
||||||
@@ -106,10 +107,11 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_tree": 1,
|
"is_tree": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-03-18 18:10:49.228407",
|
"modified": "2022-12-24 11:16:12.486719",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Setup",
|
"module": "Setup",
|
||||||
"name": "Supplier Group",
|
"name": "Supplier Group",
|
||||||
|
"naming_rule": "By fieldname",
|
||||||
"nsm_parent_field": "parent_supplier_group",
|
"nsm_parent_field": "parent_supplier_group",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
@@ -156,8 +158,18 @@
|
|||||||
"permlevel": 1,
|
"permlevel": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"role": "Purchase User"
|
"role": "Purchase User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Accounts User",
|
||||||
|
"share": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"show_name_in_global_search": 1,
|
"show_name_in_global_search": 1,
|
||||||
"sort_order": "ASC"
|
"sort_field": "modified",
|
||||||
|
"sort_order": "ASC",
|
||||||
|
"states": []
|
||||||
}
|
}
|
||||||
@@ -123,11 +123,12 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_tree": 1,
|
"is_tree": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-02-08 17:10:03.767426",
|
"modified": "2022-12-24 11:16:39.964956",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Setup",
|
"module": "Setup",
|
||||||
"name": "Territory",
|
"name": "Territory",
|
||||||
"name_case": "Title Case",
|
"name_case": "Title Case",
|
||||||
|
"naming_rule": "By fieldname",
|
||||||
"nsm_parent_field": "parent_territory",
|
"nsm_parent_field": "parent_territory",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
@@ -175,10 +176,19 @@
|
|||||||
"role": "Customer",
|
"role": "Customer",
|
||||||
"select": 1,
|
"select": 1,
|
||||||
"share": 1
|
"share": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Accounts User",
|
||||||
|
"share": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"search_fields": "parent_territory,territory_manager",
|
"search_fields": "parent_territory,territory_manager",
|
||||||
"show_name_in_global_search": 1,
|
"show_name_in_global_search": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC"
|
"sort_order": "DESC",
|
||||||
|
"states": []
|
||||||
}
|
}
|
||||||
@@ -162,6 +162,7 @@ def update_qty(bin_name, args):
|
|||||||
.where((sle.item_code == args.get("item_code")) & (sle.warehouse == args.get("warehouse")))
|
.where((sle.item_code == args.get("item_code")) & (sle.warehouse == args.get("warehouse")))
|
||||||
.orderby(CombineDatetime(sle.posting_date, sle.posting_time), order=Order.desc)
|
.orderby(CombineDatetime(sle.posting_date, sle.posting_time), order=Order.desc)
|
||||||
.orderby(sle.creation, order=Order.desc)
|
.orderby(sle.creation, order=Order.desc)
|
||||||
|
.limit(1)
|
||||||
.run()
|
.run()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,15 @@ frappe.ui.form.on('Pick List', {
|
|||||||
if (!(frm.doc.locations && frm.doc.locations.length)) {
|
if (!(frm.doc.locations && frm.doc.locations.length)) {
|
||||||
frappe.msgprint(__('Add items in the Item Locations table'));
|
frappe.msgprint(__('Add items in the Item Locations table'));
|
||||||
} else {
|
} else {
|
||||||
frm.call('set_item_locations', {save: save});
|
frappe.call({
|
||||||
|
method: "set_item_locations",
|
||||||
|
doc: frm.doc,
|
||||||
|
args: {
|
||||||
|
"save": save,
|
||||||
|
},
|
||||||
|
freeze: 1,
|
||||||
|
freeze_message: __("Setting Item Locations..."),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
get_item_locations: (frm) => {
|
get_item_locations: (frm) => {
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ class PickList(Document):
|
|||||||
|
|
||||||
# reset
|
# reset
|
||||||
self.delete_key("locations")
|
self.delete_key("locations")
|
||||||
|
updated_locations = frappe._dict()
|
||||||
for item_doc in items:
|
for item_doc in items:
|
||||||
item_code = item_doc.item_code
|
item_code = item_doc.item_code
|
||||||
|
|
||||||
@@ -155,7 +156,26 @@ class PickList(Document):
|
|||||||
for row in locations:
|
for row in locations:
|
||||||
location = item_doc.as_dict()
|
location = item_doc.as_dict()
|
||||||
location.update(row)
|
location.update(row)
|
||||||
self.append("locations", location)
|
key = (
|
||||||
|
location.item_code,
|
||||||
|
location.warehouse,
|
||||||
|
location.uom,
|
||||||
|
location.batch_no,
|
||||||
|
location.serial_no,
|
||||||
|
location.sales_order_item or location.material_request_item,
|
||||||
|
)
|
||||||
|
|
||||||
|
if key not in updated_locations:
|
||||||
|
updated_locations.setdefault(key, location)
|
||||||
|
else:
|
||||||
|
updated_locations[key].qty += location.qty
|
||||||
|
updated_locations[key].stock_qty += location.stock_qty
|
||||||
|
|
||||||
|
for location in updated_locations.values():
|
||||||
|
if location.picked_qty > location.stock_qty:
|
||||||
|
location.picked_qty = location.stock_qty
|
||||||
|
|
||||||
|
self.append("locations", location)
|
||||||
|
|
||||||
# If table is empty on update after submit, set stock_qty, picked_qty to 0 so that indicator is red
|
# If table is empty on update after submit, set stock_qty, picked_qty to 0 so that indicator is red
|
||||||
# and give feedback to the user. This is to avoid empty Pick Lists.
|
# and give feedback to the user. This is to avoid empty Pick Lists.
|
||||||
|
|||||||
@@ -112,6 +112,10 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
attach_bom_items(frm.doc.bom_no);
|
attach_bom_items(frm.doc.bom_no);
|
||||||
|
|
||||||
|
if(!check_should_not_attach_bom_items(frm.doc.bom_no)) {
|
||||||
|
erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setup_quality_inspection: function(frm) {
|
setup_quality_inspection: function(frm) {
|
||||||
@@ -326,7 +330,11 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
}
|
}
|
||||||
|
|
||||||
frm.trigger("setup_quality_inspection");
|
frm.trigger("setup_quality_inspection");
|
||||||
attach_bom_items(frm.doc.bom_no)
|
attach_bom_items(frm.doc.bom_no);
|
||||||
|
|
||||||
|
if(!check_should_not_attach_bom_items(frm.doc.bom_no)) {
|
||||||
|
erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
before_save: function(frm) {
|
before_save: function(frm) {
|
||||||
@@ -939,7 +947,10 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle
|
|||||||
method: "get_items",
|
method: "get_items",
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if(!r.exc) refresh_field("items");
|
if(!r.exc) refresh_field("items");
|
||||||
if(me.frm.doc.bom_no) attach_bom_items(me.frm.doc.bom_no)
|
if(me.frm.doc.bom_no) {
|
||||||
|
attach_bom_items(me.frm.doc.bom_no);
|
||||||
|
erpnext.accounts.dimensions.update_dimension(me.frm, me.frm.doctype);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -470,8 +470,10 @@ class update_entries_after(object):
|
|||||||
item_code = %(item_code)s
|
item_code = %(item_code)s
|
||||||
and warehouse = %(warehouse)s
|
and warehouse = %(warehouse)s
|
||||||
and is_cancelled = 0
|
and is_cancelled = 0
|
||||||
and timestamp(posting_date, time_format(posting_time, %(time_format)s)) = timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
|
and (
|
||||||
|
posting_date = %(posting_date)s and
|
||||||
|
time_format(posting_time, %(time_format)s) = time_format(%(posting_time)s, %(time_format)s)
|
||||||
|
)
|
||||||
order by
|
order by
|
||||||
creation ASC
|
creation ASC
|
||||||
for update
|
for update
|
||||||
@@ -1070,7 +1072,13 @@ def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False):
|
|||||||
and warehouse = %(warehouse)s
|
and warehouse = %(warehouse)s
|
||||||
and is_cancelled = 0
|
and is_cancelled = 0
|
||||||
{voucher_condition}
|
{voucher_condition}
|
||||||
and timestamp(posting_date, time_format(posting_time, %(time_format)s)) < timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
|
and (
|
||||||
|
posting_date < %(posting_date)s or
|
||||||
|
(
|
||||||
|
posting_date = %(posting_date)s and
|
||||||
|
time_format(posting_time, %(time_format)s) < time_format(%(posting_time)s, %(time_format)s)
|
||||||
|
)
|
||||||
|
)
|
||||||
order by timestamp(posting_date, posting_time) desc, creation desc
|
order by timestamp(posting_date, posting_time) desc, creation desc
|
||||||
limit 1
|
limit 1
|
||||||
for update""".format(
|
for update""".format(
|
||||||
@@ -1355,8 +1363,13 @@ def update_qty_in_future_sle(args, allow_negative_stock=False):
|
|||||||
and warehouse = %(warehouse)s
|
and warehouse = %(warehouse)s
|
||||||
and voucher_no != %(voucher_no)s
|
and voucher_no != %(voucher_no)s
|
||||||
and is_cancelled = 0
|
and is_cancelled = 0
|
||||||
and timestamp(posting_date, time_format(posting_time, %(time_format)s))
|
and (
|
||||||
> timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
|
posting_date > %(posting_date)s or
|
||||||
|
(
|
||||||
|
posting_date = %(posting_date)s and
|
||||||
|
time_format(posting_time, %(time_format)s) > time_format(%(posting_time)s, %(time_format)s)
|
||||||
|
)
|
||||||
|
)
|
||||||
{datetime_limit_condition}
|
{datetime_limit_condition}
|
||||||
""",
|
""",
|
||||||
args,
|
args,
|
||||||
|
|||||||
Reference in New Issue
Block a user