mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-20 21:24:02 +00:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
616448f9c9 | ||
|
|
70a8c64ea4 | ||
|
|
00a75d4984 | ||
|
|
8b826c2369 | ||
|
|
210786119d | ||
|
|
85c509ea69 | ||
|
|
6e35b6b6b3 | ||
|
|
94605687c7 | ||
|
|
e27518b2d4 | ||
|
|
cf71178af6 | ||
|
|
002575244f | ||
|
|
aadc83d0d9 | ||
|
|
66f1e2a076 | ||
|
|
1d5935a10f | ||
|
|
d82106cb76 | ||
|
|
03aeac6c9b | ||
|
|
014f5bff5c | ||
|
|
819667ab24 | ||
|
|
aa20e224fb | ||
|
|
dcae024fd2 | ||
|
|
30a6fe55ca | ||
|
|
57f9894f41 | ||
|
|
2c383a69f9 | ||
|
|
b7d76d8fad | ||
|
|
9b5183e0e5 | ||
|
|
0b43d518af | ||
|
|
a6b490e4b3 | ||
|
|
a5d33d5665 | ||
|
|
48c3c80383 | ||
|
|
a9a68f2523 | ||
|
|
f3da431f1f | ||
|
|
14e92d589f | ||
|
|
3d51c1b363 | ||
|
|
825ef415ef | ||
|
|
f6148c4352 | ||
|
|
3c408ff66b | ||
|
|
f6bc9fc505 | ||
|
|
0c7911d0fe | ||
|
|
b8ecc4274f | ||
|
|
a9b7bc1385 | ||
|
|
d062e50f35 | ||
|
|
646de0bec1 | ||
|
|
02de47c673 | ||
|
|
42b38b7998 | ||
|
|
180f406917 | ||
|
|
90803b852e | ||
|
|
4cbde47ac4 | ||
|
|
0b3215cd42 | ||
|
|
b9f9be3d88 | ||
|
|
faeedb01a8 | ||
|
|
a2402ddf52 | ||
|
|
6b29c06511 | ||
|
|
2cedd455d9 | ||
|
|
ba31f685f8 | ||
|
|
507d27d509 | ||
|
|
e27dd64044 | ||
|
|
7f617a54d1 | ||
|
|
7986e69839 | ||
|
|
836545bdb4 | ||
|
|
4fccef0636 | ||
|
|
2b6b0b32a7 |
@@ -3,7 +3,7 @@ import inspect
|
||||
|
||||
import frappe
|
||||
|
||||
__version__ = "14.89.2"
|
||||
__version__ = "14.92.2"
|
||||
|
||||
|
||||
def get_default_company(user=None):
|
||||
|
||||
@@ -55,46 +55,46 @@ class Dunning(AccountsController):
|
||||
"conversion_rate",
|
||||
"cost_center",
|
||||
]
|
||||
inv = frappe.db.get_value("Sales Invoice", self.sales_invoice, invoice_fields, as_dict=1)
|
||||
|
||||
accounting_dimensions = get_accounting_dimensions()
|
||||
invoice_fields.extend(accounting_dimensions)
|
||||
|
||||
inv = frappe.db.get_value("Sales Invoice", self.sales_invoice, invoice_fields, as_dict=1)
|
||||
|
||||
dunning_in_company_currency = flt(self.dunning_amount * inv.conversion_rate)
|
||||
default_cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": inv.debit_to,
|
||||
"party_type": "Customer",
|
||||
"party": self.customer,
|
||||
"due_date": self.due_date,
|
||||
"against": self.income_account,
|
||||
"debit": dunning_in_company_currency,
|
||||
"debit_in_account_currency": self.dunning_amount,
|
||||
"against_voucher": self.name,
|
||||
"against_voucher_type": "Dunning",
|
||||
"cost_center": inv.cost_center or default_cost_center,
|
||||
"project": inv.project,
|
||||
},
|
||||
inv.party_account_currency,
|
||||
item=inv,
|
||||
)
|
||||
)
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": self.income_account,
|
||||
"against": self.customer,
|
||||
"credit": dunning_in_company_currency,
|
||||
"cost_center": inv.cost_center or default_cost_center,
|
||||
"credit_in_account_currency": self.dunning_amount,
|
||||
"project": inv.project,
|
||||
},
|
||||
item=inv,
|
||||
)
|
||||
)
|
||||
debit = {
|
||||
"account": inv.debit_to,
|
||||
"party_type": "Customer",
|
||||
"party": self.customer,
|
||||
"due_date": self.due_date,
|
||||
"against": self.income_account,
|
||||
"debit": dunning_in_company_currency,
|
||||
"debit_in_account_currency": self.dunning_amount,
|
||||
"against_voucher": self.name,
|
||||
"against_voucher_type": "Dunning",
|
||||
"cost_center": inv.cost_center or default_cost_center,
|
||||
"project": inv.project,
|
||||
}
|
||||
|
||||
credit = {
|
||||
"account": self.income_account,
|
||||
"against": self.customer,
|
||||
"credit": dunning_in_company_currency,
|
||||
"credit_in_account_currency": self.dunning_amount,
|
||||
"cost_center": inv.cost_center or default_cost_center,
|
||||
"project": inv.project,
|
||||
}
|
||||
|
||||
for dimension in accounting_dimensions:
|
||||
if val := inv.get(dimension):
|
||||
debit[dimension] = credit[dimension] = val
|
||||
|
||||
gl_entries = [
|
||||
self.get_gl_dict(debit, inv.party_account_currency, item=inv),
|
||||
self.get_gl_dict(credit, item=inv),
|
||||
]
|
||||
make_gl_entries(
|
||||
gl_entries, cancel=(self.docstatus == 2), update_outstanding="No", merge_entries=False
|
||||
)
|
||||
|
||||
@@ -261,7 +261,7 @@ def validate_balance_type(account, adv_adj=False):
|
||||
if balance_must_be:
|
||||
balance = frappe.db.sql(
|
||||
"""select sum(debit) - sum(credit)
|
||||
from `tabGL Entry` where account = %s""",
|
||||
from `tabGL Entry` where is_cancelled = 0 and account = %s""",
|
||||
account,
|
||||
)[0][0]
|
||||
|
||||
|
||||
@@ -60,6 +60,6 @@ def create_party_link(primary_role, primary_party, secondary_party):
|
||||
party_link.secondary_role = "Customer" if primary_role == "Supplier" else "Supplier"
|
||||
party_link.secondary_party = secondary_party
|
||||
|
||||
party_link.save(ignore_permissions=True)
|
||||
party_link.save()
|
||||
|
||||
return party_link
|
||||
|
||||
@@ -330,7 +330,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
payment_type: function(frm) {
|
||||
set_default_party_type(frm);
|
||||
if(frm.doc.payment_type == "Internal Transfer") {
|
||||
$.each(["party", "party_balance", "paid_from", "paid_to",
|
||||
$.each(["party", "party_type", "party_balance", "paid_from", "paid_to",
|
||||
"references", "total_allocated_amount"], function(i, field) {
|
||||
frm.set_value(field, null);
|
||||
});
|
||||
|
||||
@@ -10,14 +10,19 @@
|
||||
"description",
|
||||
"section_break_4",
|
||||
"due_date",
|
||||
"invoice_portion",
|
||||
"mode_of_payment",
|
||||
"column_break_5",
|
||||
"invoice_portion",
|
||||
"due_date_based_on",
|
||||
"credit_days",
|
||||
"credit_months",
|
||||
"section_break_6",
|
||||
"discount_type",
|
||||
"discount_date",
|
||||
"column_break_9",
|
||||
"discount",
|
||||
"discount_type",
|
||||
"column_break_9",
|
||||
"discount_validity_based_on",
|
||||
"discount_validity",
|
||||
"section_break_9",
|
||||
"payment_amount",
|
||||
"outstanding",
|
||||
@@ -155,12 +160,50 @@
|
||||
"fieldtype": "Currency",
|
||||
"label": "Payment Amount (Company Currency)",
|
||||
"options": "Company:company:default_currency"
|
||||
},
|
||||
{
|
||||
"fieldname": "due_date_based_on",
|
||||
"fieldtype": "Select",
|
||||
"label": "Due Date Based On",
|
||||
"options": "\nDay(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:in_list(['Day(s) after invoice date', 'Day(s) after the end of the invoice month'], doc.due_date_based_on)",
|
||||
"fieldname": "credit_days",
|
||||
"fieldtype": "Int",
|
||||
"label": "Credit Days",
|
||||
"non_negative": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.due_date_based_on=='Month(s) after the end of the invoice month'",
|
||||
"fieldname": "credit_months",
|
||||
"fieldtype": "Int",
|
||||
"label": "Credit Months",
|
||||
"non_negative": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "discount",
|
||||
"fieldname": "discount_validity_based_on",
|
||||
"fieldtype": "Select",
|
||||
"label": "Discount Validity Based On",
|
||||
"options": "\nDay(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "discount_validity_based_on",
|
||||
"fieldname": "discount_validity",
|
||||
"fieldtype": "Int",
|
||||
"label": "Discount Validity",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-09-16 13:57:06.382859",
|
||||
"modified": "2025-07-31 08:38:25.820701",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Schedule",
|
||||
@@ -171,4 +214,4 @@
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,4 +161,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,7 +560,13 @@ class POSInvoice(SalesInvoice):
|
||||
"Account", self.debit_to, "account_currency", cache=True
|
||||
)
|
||||
if not self.due_date and self.customer:
|
||||
self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
|
||||
self.due_date = get_due_date(
|
||||
self.posting_date,
|
||||
"Customer",
|
||||
self.customer,
|
||||
self.company,
|
||||
template_name=self.payment_terms_template,
|
||||
)
|
||||
|
||||
super(SalesInvoice, self).set_missing_values(for_validate)
|
||||
|
||||
|
||||
@@ -173,7 +173,12 @@ class PurchaseInvoice(BuyingController):
|
||||
)
|
||||
if not self.due_date:
|
||||
self.due_date = get_due_date(
|
||||
self.posting_date, "Supplier", self.supplier, self.company, self.bill_date
|
||||
self.posting_date,
|
||||
"Supplier",
|
||||
self.supplier,
|
||||
self.company,
|
||||
self.bill_date,
|
||||
template_name=self.payment_terms_template,
|
||||
)
|
||||
|
||||
tds_category = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
|
||||
|
||||
@@ -1816,19 +1816,16 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
rate = flt(sle.stock_value_difference) / flt(sle.actual_qty)
|
||||
self.assertAlmostEqual(rate, 500)
|
||||
|
||||
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
|
||||
def test_payment_allocation_for_payment_terms(self):
|
||||
from erpnext.buying.doctype.purchase_order.test_purchase_order import (
|
||||
create_pr_against_po,
|
||||
create_purchase_order,
|
||||
)
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
||||
automatically_fetch_payment_terms,
|
||||
)
|
||||
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
|
||||
make_purchase_invoice as make_pi_from_pr,
|
||||
)
|
||||
|
||||
automatically_fetch_payment_terms()
|
||||
frappe.db.set_value(
|
||||
"Payment Terms Template",
|
||||
"_Test Payment Term Template",
|
||||
@@ -1854,7 +1851,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
pi = make_pi_from_pr(pr.name)
|
||||
self.assertEqual(pi.payment_schedule[0].payment_amount, 1000)
|
||||
|
||||
automatically_fetch_payment_terms(enable=0)
|
||||
frappe.db.set_value(
|
||||
"Payment Terms Template",
|
||||
"_Test Payment Term Template",
|
||||
|
||||
@@ -4,15 +4,25 @@
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"mandatory": 1,
|
||||
"options": "Company",
|
||||
"wildcard_filter": 0
|
||||
}
|
||||
],
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2019-01-17 17:20:42.374958",
|
||||
"modified": "2025-08-28 19:06:54.273322",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Trial Balance (Simple)",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"query": "select fiscal_year as \"Fiscal Year:Data:80\",\n\tcompany as \"Company:Data:220\",\n\tposting_date as \"Posting Date:Date:100\",\n\taccount as \"Account:Data:380\",\n\tsum(debit) as \"Debit:Currency:140\",\n\tsum(credit) as \"Credit:Currency:140\",\n\tfinance_book as \"Finance Book:Link/Finance Book:140\"\nfrom `tabGL Entry`\ngroup by fiscal_year, company, posting_date, account\norder by fiscal_year, company, posting_date, account",
|
||||
"query": "select fiscal_year as \"Fiscal Year:Data:80\",\n\tcompany as \"Company:Data:220\",\n\tposting_date as \"Posting Date:Date:100\",\n\taccount as \"Account:Data:380\",\n\tsum(debit) as \"Debit:Currency:140\",\n\tsum(credit) as \"Credit:Currency:140\",\n\tfinance_book as \"Finance Book:Link/Finance Book:140\"\nfrom `tabGL Entry`\nwhere is_cancelled = 0 and company = %(company)s\ngroup by fiscal_year, company, posting_date, account\norder by fiscal_year, company, posting_date, account",
|
||||
"ref_doctype": "GL Entry",
|
||||
"report_name": "Trial Balance (Simple)",
|
||||
"report_type": "Query Report",
|
||||
|
||||
@@ -2219,6 +2219,10 @@ def build_qb_match_conditions(doctype, user=None) -> list:
|
||||
for filter in match_filters:
|
||||
for link_option, allowed_values in filter.items():
|
||||
fieldnames = link_fields_map.get(link_option, [])
|
||||
cond = None
|
||||
|
||||
if link_option == doctype:
|
||||
cond = _dt["name"].isin(allowed_values)
|
||||
|
||||
for fieldname in fieldnames:
|
||||
field = _dt[fieldname]
|
||||
@@ -2227,6 +2231,7 @@ def build_qb_match_conditions(doctype, user=None) -> list:
|
||||
if not apply_strict_user_permissions:
|
||||
cond = (Coalesce(field, "") == "") | cond
|
||||
|
||||
if cond:
|
||||
criterion.append(cond)
|
||||
|
||||
return criterion
|
||||
|
||||
@@ -456,9 +456,8 @@ class PurchaseOrder(BuyingController):
|
||||
if not self.is_against_so():
|
||||
return
|
||||
for item in removed_items:
|
||||
prev_ordered_qty = (
|
||||
prev_ordered_qty = flt(
|
||||
frappe.get_cached_value("Sales Order Item", item.get("sales_order_item"), "ordered_qty")
|
||||
or 0.0
|
||||
)
|
||||
|
||||
frappe.db.set_value(
|
||||
|
||||
@@ -526,12 +526,8 @@ class TestPurchaseOrder(FrappeTestCase):
|
||||
self.assertRaises(frappe.ValidationError, pr.submit)
|
||||
self.assertRaises(frappe.ValidationError, pi.submit)
|
||||
|
||||
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
|
||||
def test_make_purchase_invoice_with_terms(self):
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
||||
automatically_fetch_payment_terms,
|
||||
)
|
||||
|
||||
automatically_fetch_payment_terms()
|
||||
po = create_purchase_order(do_not_save=True)
|
||||
|
||||
self.assertRaises(frappe.ValidationError, make_pi_from_po, po.name)
|
||||
@@ -555,7 +551,6 @@ class TestPurchaseOrder(FrappeTestCase):
|
||||
self.assertEqual(getdate(pi.payment_schedule[0].due_date), getdate(po.transaction_date))
|
||||
self.assertEqual(pi.payment_schedule[1].payment_amount, 2500.0)
|
||||
self.assertEqual(getdate(pi.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
|
||||
automatically_fetch_payment_terms(enable=0)
|
||||
|
||||
def test_warehouse_company_validation(self):
|
||||
from erpnext.stock.utils import InvalidWarehouseCompany
|
||||
@@ -703,6 +698,7 @@ class TestPurchaseOrder(FrappeTestCase):
|
||||
)
|
||||
self.assertEqual(due_date, "2023-03-31")
|
||||
|
||||
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 0})
|
||||
def test_terms_are_not_copied_if_automatically_fetch_payment_terms_is_unchecked(self):
|
||||
po = create_purchase_order(do_not_save=1)
|
||||
po.payment_terms_template = "_Test Payment Term Template"
|
||||
@@ -834,18 +830,16 @@ class TestPurchaseOrder(FrappeTestCase):
|
||||
bo.load_from_db()
|
||||
self.assertEqual(bo.items[0].ordered_qty, 5)
|
||||
|
||||
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
|
||||
def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
|
||||
create_payment_terms_template,
|
||||
)
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
||||
automatically_fetch_payment_terms,
|
||||
compare_payment_schedules,
|
||||
)
|
||||
|
||||
automatically_fetch_payment_terms()
|
||||
|
||||
po = create_purchase_order(qty=10, rate=100, do_not_save=1)
|
||||
create_payment_terms_template()
|
||||
po.payment_terms_template = "Test Receivable Template"
|
||||
@@ -859,8 +853,6 @@ class TestPurchaseOrder(FrappeTestCase):
|
||||
# self.assertEqual(po.payment_terms_template, pi.payment_terms_template)
|
||||
compare_payment_schedules(self, po, pi)
|
||||
|
||||
automatically_fetch_payment_terms(enable=0)
|
||||
|
||||
def test_internal_transfer_flow(self):
|
||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
|
||||
make_inter_company_purchase_invoice,
|
||||
|
||||
@@ -42,6 +42,11 @@ frappe.ui.form.on("Supplier", {
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
frm.make_methods = {
|
||||
"Bank Account": () => erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name),
|
||||
"Pricing Rule": () => erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name),
|
||||
};
|
||||
},
|
||||
|
||||
refresh: function (frm) {
|
||||
@@ -84,21 +89,9 @@ frappe.ui.form.on("Supplier", {
|
||||
__("View")
|
||||
);
|
||||
|
||||
frm.add_custom_button(
|
||||
__("Bank Account"),
|
||||
function () {
|
||||
erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name);
|
||||
},
|
||||
__("Create")
|
||||
);
|
||||
frm.add_custom_button(__("Bank Account"), () => frm.make_methods["Bank Account"](), __("Create"));
|
||||
|
||||
frm.add_custom_button(
|
||||
__("Pricing Rule"),
|
||||
function () {
|
||||
erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name);
|
||||
},
|
||||
__("Create")
|
||||
);
|
||||
frm.add_custom_button(__("Pricing Rule"), () => frm.make_methods["Pricing Rule"](), __("Create"));
|
||||
|
||||
frm.add_custom_button(
|
||||
__("Get Supplier Group Details"),
|
||||
@@ -108,7 +101,10 @@ frappe.ui.form.on("Supplier", {
|
||||
__("Actions")
|
||||
);
|
||||
|
||||
if (cint(frappe.defaults.get_default("enable_common_party_accounting"))) {
|
||||
if (
|
||||
cint(frappe.defaults.get_default("enable_common_party_accounting")) &&
|
||||
frappe.model.can_create("Party Link")
|
||||
) {
|
||||
frm.add_custom_button(
|
||||
__("Link with Customer"),
|
||||
function () {
|
||||
|
||||
@@ -213,6 +213,11 @@ class AccountsController(TransactionBase):
|
||||
|
||||
self.validate_date_with_fiscal_year()
|
||||
self.validate_party_accounts()
|
||||
if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||
if self.is_return:
|
||||
self.validate_qty()
|
||||
else:
|
||||
self.validate_deferred_start_and_end_date()
|
||||
|
||||
self.validate_inter_company_reference()
|
||||
|
||||
@@ -258,11 +263,6 @@ class AccountsController(TransactionBase):
|
||||
|
||||
self.set_advance_gain_or_loss()
|
||||
|
||||
if self.is_return:
|
||||
self.validate_qty()
|
||||
else:
|
||||
self.validate_deferred_start_and_end_date()
|
||||
|
||||
self.validate_deferred_income_expense_account()
|
||||
self.set_inter_company_account()
|
||||
|
||||
@@ -2239,6 +2239,7 @@ class AccountsController(TransactionBase):
|
||||
|
||||
self.payment_schedule = []
|
||||
self.payment_terms_template = po_or_so.payment_terms_template
|
||||
posting_date = self.get("bill_date") or self.get("posting_date") or self.get("transaction_date")
|
||||
|
||||
for schedule in po_or_so.payment_schedule:
|
||||
payment_schedule = {
|
||||
@@ -2251,6 +2252,17 @@ class AccountsController(TransactionBase):
|
||||
}
|
||||
|
||||
if automatically_fetch_payment_terms:
|
||||
if schedule.due_date_based_on:
|
||||
payment_schedule["due_date"] = get_due_date(schedule, posting_date)
|
||||
payment_schedule["due_date_based_on"] = schedule.due_date_based_on
|
||||
payment_schedule["credit_days"] = cint(schedule.credit_days)
|
||||
payment_schedule["credit_months"] = cint(schedule.credit_months)
|
||||
|
||||
if schedule.discount_validity_based_on:
|
||||
payment_schedule["discount_date"] = get_discount_date(schedule, posting_date)
|
||||
payment_schedule["discount_validity_based_on"] = schedule.discount_validity_based_on
|
||||
payment_schedule["discount_validity"] = cint(schedule.discount_validity)
|
||||
|
||||
payment_schedule["payment_amount"] = flt(
|
||||
grand_total * flt(payment_schedule["invoice_portion"]) / 100,
|
||||
schedule.precision("payment_amount"),
|
||||
@@ -2738,9 +2750,7 @@ def set_balance_in_account_currency(
|
||||
_("Account: {0} with currency: {1} can not be selected").format(gl_dict.account, account_currency)
|
||||
)
|
||||
|
||||
gl_dict["account_currency"] = (
|
||||
company_currency if account_currency == company_currency else account_currency
|
||||
)
|
||||
gl_dict["account_currency"] = account_currency
|
||||
|
||||
# set debit/credit in account currency if not provided
|
||||
if flt(gl_dict.debit) and not flt(gl_dict.debit_in_account_currency):
|
||||
@@ -2987,14 +2997,26 @@ def get_payment_term_details(
|
||||
term = frappe.get_doc("Payment Term", term)
|
||||
else:
|
||||
term_details.payment_term = term.payment_term
|
||||
term_details.description = term.description
|
||||
term_details.invoice_portion = term.invoice_portion
|
||||
|
||||
fields_to_copy = [
|
||||
"description",
|
||||
"invoice_portion",
|
||||
"discount_type",
|
||||
"discount",
|
||||
"mode_of_payment",
|
||||
"due_date_based_on",
|
||||
"credit_days",
|
||||
"credit_months",
|
||||
"discount_validity_based_on",
|
||||
"discount_validity",
|
||||
]
|
||||
|
||||
for field in fields_to_copy:
|
||||
term_details[field] = term.get(field)
|
||||
|
||||
term_details.payment_amount = flt(term.invoice_portion) * flt(grand_total) / 100
|
||||
term_details.base_payment_amount = flt(term.invoice_portion) * flt(base_grand_total) / 100
|
||||
term_details.discount_type = term.discount_type
|
||||
term_details.discount = term.discount
|
||||
term_details.outstanding = term_details.payment_amount
|
||||
term_details.mode_of_payment = term.mode_of_payment
|
||||
|
||||
if bill_date:
|
||||
term_details.due_date = get_due_date(term, bill_date)
|
||||
@@ -3013,11 +3035,11 @@ def get_due_date(term, posting_date=None, bill_date=None):
|
||||
due_date = None
|
||||
date = bill_date or posting_date
|
||||
if term.due_date_based_on == "Day(s) after invoice date":
|
||||
due_date = add_days(date, term.credit_days)
|
||||
due_date = add_days(date, cint(term.credit_days))
|
||||
elif term.due_date_based_on == "Day(s) after the end of the invoice month":
|
||||
due_date = add_days(get_last_day(date), term.credit_days)
|
||||
due_date = add_days(get_last_day(date), cint(term.credit_days))
|
||||
elif term.due_date_based_on == "Month(s) after the end of the invoice month":
|
||||
due_date = get_last_day(add_months(date, term.credit_months))
|
||||
due_date = get_last_day(add_months(date, cint(term.credit_months)))
|
||||
return due_date
|
||||
|
||||
|
||||
@@ -3025,11 +3047,11 @@ def get_discount_date(term, posting_date=None, bill_date=None):
|
||||
discount_validity = None
|
||||
date = bill_date or posting_date
|
||||
if term.discount_validity_based_on == "Day(s) after invoice date":
|
||||
discount_validity = add_days(date, term.discount_validity)
|
||||
discount_validity = add_days(date, cint(term.discount_validity))
|
||||
elif term.discount_validity_based_on == "Day(s) after the end of the invoice month":
|
||||
discount_validity = add_days(get_last_day(date), term.discount_validity)
|
||||
discount_validity = add_days(get_last_day(date), cint(term.discount_validity))
|
||||
elif term.discount_validity_based_on == "Month(s) after the end of the invoice month":
|
||||
discount_validity = get_last_day(add_months(date, term.discount_validity))
|
||||
discount_validity = get_last_day(add_months(date, cint(term.discount_validity)))
|
||||
return discount_validity
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"code_list",
|
||||
"canonical_uri",
|
||||
"title",
|
||||
"common_code",
|
||||
"description",
|
||||
@@ -71,10 +72,17 @@
|
||||
"in_list_view": 1,
|
||||
"label": "Description",
|
||||
"max_height": "60px"
|
||||
},
|
||||
{
|
||||
"fetch_from": "code_list.canonical_uri",
|
||||
"fieldname": "canonical_uri",
|
||||
"fieldtype": "Data",
|
||||
"label": "Canonical URI"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"links": [],
|
||||
"modified": "2024-11-06 07:46:17.175687",
|
||||
"modified": "2025-10-04 17:22:28.176155",
|
||||
"modified_by": "Administrator",
|
||||
"module": "EDI",
|
||||
"name": "Common Code",
|
||||
@@ -94,10 +102,11 @@
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
"search_fields": "common_code,description",
|
||||
"show_title_field_in_link": 1,
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"title_field": "title"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ class CommonCode(Document):
|
||||
|
||||
additional_data: DF.Code | None
|
||||
applies_to: DF.Table[DynamicLink]
|
||||
canonical_uri: DF.Data | None
|
||||
code_list: DF.Link
|
||||
common_code: DF.Data
|
||||
description: DF.SmallText | None
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import urllib.parse
|
||||
|
||||
import frappe
|
||||
|
||||
|
||||
def get_context(context):
|
||||
if frappe.form_dict.project:
|
||||
context.parents = [
|
||||
{"title": frappe.form_dict.project, "route": "/projects?project=" + frappe.form_dict.project}
|
||||
]
|
||||
context.success_url = "/projects?project=" + frappe.form_dict.project
|
||||
if project := frappe.form_dict.project:
|
||||
title = frappe.utils.data.escape_html(project)
|
||||
route = "/projects?" + urllib.parse.urlencode({"project": project})
|
||||
context.parents = [{"title": title, "route": route}]
|
||||
context.success_url = route
|
||||
|
||||
elif context.doc and context.doc.get("project"):
|
||||
context.parents = [
|
||||
{"title": context.doc.project, "route": "/projects?project=" + context.doc.project}
|
||||
]
|
||||
context.success_url = "/projects?project=" + context.doc.project
|
||||
elif context.doc and (project := context.doc.get("project")):
|
||||
title = frappe.utils.data.escape_html(project)
|
||||
route = "/projects?" + urllib.parse.urlencode({"project": project})
|
||||
context.parents = [{"title": title, "route": route}]
|
||||
context.success_url = route
|
||||
|
||||
@@ -76,9 +76,10 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
|
||||
|
||||
// Update paid amount on return/debit note creation
|
||||
if (
|
||||
this.frm.doc.doctype === "Purchase Invoice"
|
||||
&& this.frm.doc.is_return
|
||||
&& (this.frm.doc.grand_total > this.frm.doc.paid_amount)
|
||||
this.frm.doc.doctype === "Purchase Invoice" &&
|
||||
this.frm.doc.is_return &&
|
||||
this.frm.doc.grand_total < 0 &&
|
||||
this.frm.doc.grand_total > this.frm.doc.paid_amount
|
||||
) {
|
||||
this.frm.doc.paid_amount = flt(this.frm.doc.grand_total, precision("grand_total"));
|
||||
}
|
||||
|
||||
@@ -878,6 +878,15 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
}
|
||||
}
|
||||
|
||||
discount_date(doc, cdt, cdn) {
|
||||
// Remove fields as discount_date is auto-managed by payment terms
|
||||
const row = locals[cdt][cdn];
|
||||
["discount_validity", "discount_validity_based_on"].forEach((field) => {
|
||||
row[field] = "";
|
||||
});
|
||||
this.frm.refresh_field("payment_schedule");
|
||||
}
|
||||
|
||||
due_date() {
|
||||
// due_date is to be changed, payment terms template and/or payment schedule must
|
||||
// be removed as due_date is automatically changed based on payment terms
|
||||
@@ -2203,6 +2212,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
'item_code': item.item_code,
|
||||
'valid_from': ["<=", doc.transaction_date || doc.bill_date || doc.posting_date],
|
||||
'item_group': item.item_group,
|
||||
'disabled': 0,
|
||||
}
|
||||
|
||||
if (doc.tax_category)
|
||||
@@ -2244,7 +2254,18 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
payment_term(doc, cdt, cdn) {
|
||||
const me = this;
|
||||
var row = locals[cdt][cdn];
|
||||
if(row.payment_term) {
|
||||
// empty date condition fields
|
||||
[
|
||||
"due_date_based_on",
|
||||
"credit_days",
|
||||
"credit_months",
|
||||
"discount_validity",
|
||||
"discount_validity_based_on",
|
||||
].forEach(function (field) {
|
||||
row[field] = "";
|
||||
});
|
||||
|
||||
if (row.payment_term) {
|
||||
frappe.call({
|
||||
method: "erpnext.controllers.accounts_controller.get_payment_term_details",
|
||||
args: {
|
||||
@@ -2254,16 +2275,19 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
grand_total: this.frm.doc.rounded_total || this.frm.doc.grand_total,
|
||||
base_grand_total: this.frm.doc.base_rounded_total || this.frm.doc.base_grand_total
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.message && !r.exc) {
|
||||
for (var d in r.message) {
|
||||
frappe.model.set_value(cdt, cdn, d, r.message[d]);
|
||||
const company_currency = me.get_company_currency();
|
||||
me.update_payment_schedule_grid_labels(company_currency);
|
||||
callback: function (r) {
|
||||
if (r.message && !r.exc) {
|
||||
const company_currency = me.get_company_currency();
|
||||
for (let d in r.message) {
|
||||
row[d] = r.message[d];
|
||||
}
|
||||
me.update_payment_schedule_grid_labels(company_currency);
|
||||
me.frm.refresh_field("payment_schedule");
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
} else {
|
||||
me.frm.refresh_field("payment_schedule");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -215,17 +215,9 @@ $.extend(erpnext.utils, {
|
||||
},
|
||||
|
||||
make_bank_account: function (doctype, docname) {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.bank_account.bank_account.make_bank_account",
|
||||
args: {
|
||||
doctype: doctype,
|
||||
docname: docname,
|
||||
},
|
||||
freeze: true,
|
||||
callback: function (r) {
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||
},
|
||||
frappe.new_doc("Bank Account", {
|
||||
party_type: doctype,
|
||||
party: docname,
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ frappe.ui.form.on("Customer", {
|
||||
method: "erpnext.selling.doctype.customer.customer.make_opportunity",
|
||||
frm: cur_frm,
|
||||
}),
|
||||
"Bank Account": () => erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name),
|
||||
};
|
||||
|
||||
frm.add_fetch("lead_name", "company_name", "customer_name");
|
||||
@@ -155,7 +156,10 @@ frappe.ui.form.on("Customer", {
|
||||
__("Actions")
|
||||
);
|
||||
|
||||
if (cint(frappe.defaults.get_default("enable_common_party_accounting"))) {
|
||||
if (
|
||||
cint(frappe.defaults.get_default("enable_common_party_accounting")) &&
|
||||
frappe.model.can_create("Party Link")
|
||||
) {
|
||||
frm.add_custom_button(
|
||||
__("Link with Supplier"),
|
||||
function () {
|
||||
|
||||
@@ -10,7 +10,7 @@ from frappe.tests.utils import FrappeTestCase, change_settings
|
||||
from frappe.utils import add_days, flt, getdate, nowdate, today
|
||||
|
||||
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
|
||||
from erpnext.controllers.accounts_controller import update_child_qty_rate
|
||||
from erpnext.controllers.accounts_controller import get_due_date, update_child_qty_rate
|
||||
from erpnext.maintenance.doctype.maintenance_schedule.test_maintenance_schedule import (
|
||||
make_maintenance_schedule,
|
||||
)
|
||||
@@ -1759,14 +1759,13 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase):
|
||||
so.load_from_db()
|
||||
self.assertRaises(frappe.LinkExistsError, so.cancel)
|
||||
|
||||
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
|
||||
def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
|
||||
create_payment_terms_template,
|
||||
)
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
|
||||
automatically_fetch_payment_terms()
|
||||
|
||||
so = make_sales_order(uom="Nos", do_not_save=1)
|
||||
create_payment_terms_template()
|
||||
so.payment_terms_template = "Test Receivable Template"
|
||||
@@ -1780,8 +1779,6 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase):
|
||||
self.assertEqual(so.payment_terms_template, si.payment_terms_template)
|
||||
compare_payment_schedules(self, so, si)
|
||||
|
||||
automatically_fetch_payment_terms(enable=0)
|
||||
|
||||
def test_zero_amount_sales_order_billing_status(self):
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
|
||||
@@ -2284,16 +2281,14 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase):
|
||||
self.assertRaises(frappe.ValidationError, so1.update_status, "Draft")
|
||||
|
||||
|
||||
def automatically_fetch_payment_terms(enable=1):
|
||||
accounts_settings = frappe.get_doc("Accounts Settings")
|
||||
accounts_settings.automatically_fetch_payment_terms = enable
|
||||
accounts_settings.save()
|
||||
|
||||
|
||||
def compare_payment_schedules(doc, doc1, doc2):
|
||||
for index, schedule in enumerate(doc1.get("payment_schedule")):
|
||||
posting_date = doc1.get("bill_date") or doc1.get("posting_date") or doc1.get("transaction_date")
|
||||
due_date = schedule.due_date
|
||||
if schedule.due_date_based_on:
|
||||
due_date = get_due_date(schedule, posting_date=posting_date)
|
||||
doc.assertEqual(schedule.payment_term, doc2.payment_schedule[index].payment_term)
|
||||
doc.assertEqual(getdate(schedule.due_date), doc2.payment_schedule[index].due_date)
|
||||
doc.assertEqual(due_date, doc2.payment_schedule[index].due_date)
|
||||
doc.assertEqual(schedule.invoice_portion, doc2.payment_schedule[index].invoice_portion)
|
||||
doc.assertEqual(schedule.payment_amount, doc2.payment_schedule[index].payment_amount)
|
||||
|
||||
|
||||
@@ -37,15 +37,15 @@ class SellingSettings(Document):
|
||||
)
|
||||
|
||||
def toggle_hide_tax_id(self):
|
||||
self.hide_tax_id = cint(self.hide_tax_id)
|
||||
_hide_tax_id = cint(self.hide_tax_id)
|
||||
|
||||
# Make property setters to hide tax_id fields
|
||||
for doctype in ("Sales Order", "Sales Invoice", "Delivery Note"):
|
||||
make_property_setter(
|
||||
doctype, "tax_id", "hidden", self.hide_tax_id, "Check", validate_fields_for_doctype=False
|
||||
doctype, "tax_id", "hidden", _hide_tax_id, "Check", validate_fields_for_doctype=False
|
||||
)
|
||||
make_property_setter(
|
||||
doctype, "tax_id", "print_hide", self.hide_tax_id, "Check", validate_fields_for_doctype=False
|
||||
doctype, "tax_id", "print_hide", _hide_tax_id, "Check", validate_fields_for_doctype=False
|
||||
)
|
||||
|
||||
def toggle_editable_rate_for_bundle_items(self):
|
||||
|
||||
@@ -21,6 +21,12 @@ erpnext.setup.EmployeeController = class EmployeeController extends frappe.ui.fo
|
||||
};
|
||||
|
||||
frappe.ui.form.on("Employee", {
|
||||
setup: function (frm) {
|
||||
frm.make_methods = {
|
||||
"Bank Account": () => erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name),
|
||||
};
|
||||
},
|
||||
|
||||
onload: function (frm) {
|
||||
frm.set_query("department", function () {
|
||||
return {
|
||||
|
||||
@@ -5,8 +5,10 @@ import unittest
|
||||
|
||||
import frappe
|
||||
import frappe.utils
|
||||
from frappe.query_builder import Criterion
|
||||
|
||||
import erpnext
|
||||
from erpnext.accounts.utils import build_qb_match_conditions
|
||||
from erpnext.setup.doctype.employee.employee import InactiveEmployeeStatusError
|
||||
|
||||
test_records = frappe.get_test_records("Employee")
|
||||
@@ -34,6 +36,32 @@ class TestEmployee(unittest.TestCase):
|
||||
employee_doc.save()
|
||||
self.assertTrue("Employee" not in frappe.get_roles(user))
|
||||
|
||||
def test_employee_user_permission(self):
|
||||
employee1 = make_employee("employee_1_test@company.com", create_user_permission=1)
|
||||
employee2 = make_employee("employee_2_test@company.com", create_user_permission=1)
|
||||
make_employee("employee_3_test@company.com", create_user_permission=1)
|
||||
|
||||
employee1_doc = frappe.get_doc("Employee", employee1)
|
||||
employee2_doc = frappe.get_doc("Employee", employee2)
|
||||
|
||||
employee2_doc.reload()
|
||||
employee2_doc.reports_to = employee1_doc.name
|
||||
employee2_doc.save()
|
||||
|
||||
frappe.set_user(employee1_doc.user_id)
|
||||
|
||||
Employee = frappe.qb.DocType("Employee")
|
||||
qb_employee_list = (
|
||||
frappe.qb.from_(Employee)
|
||||
.select(Employee.name)
|
||||
.where(Criterion.all(build_qb_match_conditions("Employee")))
|
||||
.orderby(Employee.Name)
|
||||
).run(pluck=Employee.name)
|
||||
employee_list = frappe.db.get_list("Employee", pluck="name", order_by="name")
|
||||
|
||||
self.assertEqual(qb_employee_list, employee_list)
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
def tearDown(self):
|
||||
frappe.db.rollback()
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ from erpnext.accounts.utils import get_balance_on
|
||||
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
||||
automatically_fetch_payment_terms,
|
||||
compare_payment_schedules,
|
||||
create_dn_against_so,
|
||||
make_sales_order,
|
||||
@@ -1026,14 +1025,13 @@ class TestDeliveryNote(FrappeTestCase):
|
||||
|
||||
self.assertTrue("TESTBATCH" in dn.packed_items[0].batch_no, "Batch number not added in packed item")
|
||||
|
||||
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
|
||||
def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
|
||||
create_payment_terms_template,
|
||||
)
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
|
||||
automatically_fetch_payment_terms()
|
||||
|
||||
so = make_sales_order(uom="Nos", do_not_save=1)
|
||||
create_payment_terms_template()
|
||||
so.payment_terms_template = "Test Receivable Template"
|
||||
@@ -1053,8 +1051,6 @@ class TestDeliveryNote(FrappeTestCase):
|
||||
self.assertEqual(so.payment_terms_template, si.payment_terms_template)
|
||||
compare_payment_schedules(self, so, si)
|
||||
|
||||
automatically_fetch_payment_terms(enable=0)
|
||||
|
||||
def test_returned_qty_in_return_dn(self):
|
||||
# SO ---> SI ---> DN
|
||||
# |
|
||||
|
||||
@@ -1056,6 +1056,7 @@ class TestPurchaseReceipt(FrappeTestCase):
|
||||
|
||||
self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount)
|
||||
|
||||
@change_settings("Accounts Settings", {"automatically_fetch_payment_terms": 1})
|
||||
def test_payment_terms_are_fetched_when_creating_purchase_invoice(self):
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
|
||||
create_payment_terms_template,
|
||||
@@ -1066,12 +1067,9 @@ class TestPurchaseReceipt(FrappeTestCase):
|
||||
make_pr_against_po,
|
||||
)
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import (
|
||||
automatically_fetch_payment_terms,
|
||||
compare_payment_schedules,
|
||||
)
|
||||
|
||||
automatically_fetch_payment_terms()
|
||||
|
||||
po = create_purchase_order(qty=10, rate=100, do_not_save=1)
|
||||
create_payment_terms_template()
|
||||
po.payment_terms_template = "Test Receivable Template"
|
||||
@@ -1089,8 +1087,6 @@ class TestPurchaseReceipt(FrappeTestCase):
|
||||
# self.assertEqual(po.payment_terms_template, pi.payment_terms_template)
|
||||
compare_payment_schedules(self, po, pi)
|
||||
|
||||
automatically_fetch_payment_terms(enable=0)
|
||||
|
||||
@change_settings("Stock Settings", {"allow_negative_stock": 1})
|
||||
def test_neg_to_positive(self):
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
|
||||
@@ -748,12 +748,12 @@ def get_items(warehouse, posting_date, posting_time, company, item_code=None, ig
|
||||
itemwise_batch_data = get_itemwise_batch(warehouse, posting_date, company, item_code)
|
||||
|
||||
for d in items:
|
||||
if d.item_code in itemwise_batch_data:
|
||||
if (d.item_code, d.warehouse) in itemwise_batch_data:
|
||||
valuation_rate = get_stock_balance(
|
||||
d.item_code, d.warehouse, posting_date, posting_time, with_valuation_rate=True
|
||||
)[1]
|
||||
|
||||
for row in itemwise_batch_data.get(d.item_code):
|
||||
for row in itemwise_batch_data.get((d.item_code, d.warehouse)):
|
||||
if ignore_empty_stock and not row.qty:
|
||||
continue
|
||||
|
||||
@@ -885,7 +885,7 @@ def get_itemwise_batch(warehouse, posting_date, company, item_code=None):
|
||||
columns, data = execute(filters)
|
||||
|
||||
for row in data:
|
||||
itemwise_batch_data.setdefault(row[0], []).append(
|
||||
itemwise_batch_data.setdefault((row[0], row[3]), []).append(
|
||||
frappe._dict(
|
||||
{
|
||||
"item_code": row[0],
|
||||
|
||||
@@ -635,8 +635,10 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False):
|
||||
taxes_with_no_validity = []
|
||||
|
||||
for tax in taxes:
|
||||
tax_company = frappe.get_cached_value("Item Tax Template", tax.item_tax_template, "company")
|
||||
if tax_company == args["company"]:
|
||||
disabled, tax_company = frappe.get_cached_value(
|
||||
"Item Tax Template", tax.item_tax_template, ["disabled", "company"]
|
||||
)
|
||||
if not disabled and tax_company == args["company"]:
|
||||
if tax.valid_from or tax.maximum_net_rate:
|
||||
# In purchase Invoice first preference will be given to supplier invoice date
|
||||
# if supplier date is not present then posting date
|
||||
|
||||
@@ -489,32 +489,19 @@ class update_entries_after:
|
||||
self.distinct_item_warehouses[key] = val
|
||||
self.new_items_found = True
|
||||
else:
|
||||
# Check if the dependent voucher is reposted
|
||||
# If not, then do not add it to the list
|
||||
if not self.is_dependent_voucher_reposted(dependant_sle):
|
||||
return
|
||||
|
||||
existing_sle_posting_date = self.distinct_item_warehouses[key].get("sle", {}).get("posting_date")
|
||||
|
||||
dependent_voucher_detail_nos = self.get_dependent_voucher_detail_nos(key)
|
||||
if getdate(dependant_sle.posting_date) < getdate(existing_sle_posting_date):
|
||||
if dependent_voucher_detail_nos and dependant_sle.voucher_detail_no in set(
|
||||
dependent_voucher_detail_nos
|
||||
):
|
||||
existing_sle = self.distinct_item_warehouses[key].get("sle", {})
|
||||
if getdate(existing_sle.get("posting_date")) > getdate(dependant_sle.posting_date):
|
||||
self.distinct_item_warehouses[key] = val
|
||||
self.new_items_found = True
|
||||
elif dependant_sle.voucher_type == "Stock Entry" and is_transfer_stock_entry(
|
||||
dependant_sle.voucher_no
|
||||
):
|
||||
if self.distinct_item_warehouses[key].get("transfer_entry_to_repost"):
|
||||
return
|
||||
|
||||
val.sle_changed = True
|
||||
dependent_voucher_detail_nos.append(dependant_sle.voucher_detail_no)
|
||||
val.dependent_voucher_detail_nos = dependent_voucher_detail_nos
|
||||
val["transfer_entry_to_repost"] = True
|
||||
self.distinct_item_warehouses[key] = val
|
||||
self.new_items_found = True
|
||||
elif dependant_sle.voucher_detail_no not in set(dependent_voucher_detail_nos):
|
||||
# Future dependent voucher needs to be repost to get the correct stock value
|
||||
# If dependent voucher has not reposted, then add it to the list
|
||||
dependent_voucher_detail_nos.append(dependant_sle.voucher_detail_no)
|
||||
self.new_items_found = True
|
||||
val.dependent_voucher_detail_nos = dependent_voucher_detail_nos
|
||||
self.distinct_item_warehouses[key] = val
|
||||
|
||||
def is_dependent_voucher_reposted(self, dependant_sle) -> bool:
|
||||
# Return False if the dependent voucher is not reposted
|
||||
@@ -1403,6 +1390,8 @@ def get_sle_by_voucher_detail_no(voucher_detail_no, excluded_sle=None):
|
||||
"posting_time",
|
||||
"voucher_detail_no",
|
||||
"posting_datetime as timestamp",
|
||||
"voucher_type",
|
||||
"voucher_no",
|
||||
],
|
||||
as_dict=1,
|
||||
)
|
||||
@@ -1830,3 +1819,10 @@ def get_stock_value_difference(item_code, warehouse, posting_date, posting_time,
|
||||
|
||||
difference_amount = query.run()
|
||||
return flt(difference_amount[0][0]) if difference_amount else 0
|
||||
|
||||
|
||||
@frappe.request_cache
|
||||
def is_transfer_stock_entry(voucher_no):
|
||||
purpose = frappe.get_cached_value("Stock Entry", voucher_no, "purpose")
|
||||
|
||||
return purpose in ["Material Transfer", "Material Transfer for Manufacture", "Send to Subcontractor"]
|
||||
|
||||
Reference in New Issue
Block a user