2
0
mirror of https://github.com/frappe/erpnext.git synced 2024-06-02 18:31:06 +00:00

Compare commits

...

12 Commits

Author SHA1 Message Date
Ninad Parikh
51fdc9bd8e
Merge 0d800a9105 into a853f6add8 2024-05-20 09:48:20 +00:00
ruthra kumar
a853f6add8
Merge pull request #41547 from frappe/mergify/bp/version-15-hotfix/pr-41492
refactor: For advances booked in Separate account, reconciliation date can be configured (backport #41492)
2024-05-20 15:17:38 +05:30
ruthra kumar
6c455be6a7 test: reconciliation date for advance payments
(cherry picked from commit 30aa4e031d)
2024-05-20 09:30:14 +00:00
ruthra kumar
53e5fc16d5 refactor: enable no-copy on advance payment flags
(cherry picked from commit cafa2f52e5)
2024-05-20 09:30:14 +00:00
ruthra kumar
349caf895b chore: better description
(cherry picked from commit 000c1b49dc)
2024-05-20 09:30:14 +00:00
ruthra kumar
2b307ff526 refactor: advance payments, allow configurable reconciliation date
(cherry picked from commit 070e2d4d26)
2024-05-20 09:30:13 +00:00
mergify[bot]
f121b33e29
fix: valuation rate for legacy serial nos (backport #41543) (#41545)
fix: valuation rate for legacy serial nos (#41543)

fix: valuation rate for serial nos
(cherry picked from commit 214b38f7c8)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-05-20 13:57:40 +05:30
ruthra kumar
0d55a25e6c
Merge pull request #41544 from ruthra-kumar/fix_filters_in_trends_reports
fix: Filters in trend reports
2024-05-20 11:10:24 +05:30
ruthra kumar
52d5d7a4d3 fix: Filters in trend reports 2024-05-20 10:36:42 +05:30
ruthra kumar
e5e1aadc9a
Merge pull request #41526 from frappe/mergify/bp/version-15-hotfix/pr-41523
fix: minor Dr and Cr between Purchase Receipt and Purchase Invoice (backport #41523)
2024-05-20 09:17:25 +05:30
ruthra kumar
3872cdc54a fix: minor Dr and Cr between Purchase Receipt and Purchase Invoice 2024-05-20 08:31:12 +05:30
Ninad1306
0d800a9105 fix: calculate shipping charges before discount is added 2024-05-07 15:48:52 +05:30
21 changed files with 245 additions and 64 deletions

View File

@ -20,6 +20,7 @@
"party",
"party_name",
"book_advance_payments_in_separate_party_account",
"reconcile_on_advance_payment_date",
"column_break_11",
"bank_account",
"party_bank_account",
@ -750,6 +751,7 @@
"fieldtype": "Check",
"hidden": 1,
"label": "Book Advance Payments in Separate Party Account",
"no_copy": 1,
"read_only": 1
},
{
@ -765,6 +767,16 @@
"label": "In Words",
"print_hide": 1,
"read_only": 1
},
{
"default": "0",
"fetch_from": "company.reconcile_on_advance_payment_date",
"fieldname": "reconcile_on_advance_payment_date",
"fieldtype": "Check",
"hidden": 1,
"label": "Reconcile on Advance Payment Date",
"no_copy": 1,
"read_only": 1
}
],
"index_web_pages_for_search": 1,
@ -778,7 +790,7 @@
"table_fieldname": "payment_entries"
}
],
"modified": "2024-04-11 11:25:07.366347",
"modified": "2024-05-17 10:21:11.199445",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@ -1249,13 +1249,16 @@ class PaymentEntry(AccountsController):
"voucher_detail_no": invoice.name,
}
date_field = "posting_date"
if invoice.reference_doctype in ["Sales Order", "Purchase Order"]:
date_field = "transaction_date"
posting_date = frappe.db.get_value(invoice.reference_doctype, invoice.reference_name, date_field)
if getdate(posting_date) < getdate(self.posting_date):
if self.reconcile_on_advance_payment_date:
posting_date = self.posting_date
else:
date_field = "posting_date"
if invoice.reference_doctype in ["Sales Order", "Purchase Order"]:
date_field = "transaction_date"
posting_date = frappe.db.get_value(invoice.reference_doctype, invoice.reference_name, date_field)
if getdate(posting_date) < getdate(self.posting_date):
posting_date = self.posting_date
dr_or_cr, account = self.get_dr_and_account_for_advances(invoice)
args_dict["account"] = account

View File

@ -1525,6 +1525,55 @@ class TestPaymentReconciliation(FrappeTestCase):
]
self.assertEqual(pl_entries, expected_ple)
def test_advance_payment_reconciliation_date(self):
frappe.db.set_value(
"Company",
self.company,
{
"book_advance_payments_in_separate_party_account": 1,
"default_advance_paid_account": self.advance_payable_account,
"reconcile_on_advance_payment_date": 1,
},
)
self.supplier = "_Test Supplier"
amount = 1500
pe = self.create_payment_entry(amount=amount)
pe.posting_date = add_days(nowdate(), -1)
pe.party_type = "Supplier"
pe.party = self.supplier
pe.payment_type = "Pay"
pe.paid_from = self.cash
pe.paid_to = self.advance_payable_account
pe.save().submit()
pi = self.create_purchase_invoice(qty=10, rate=100)
self.assertNotEqual(pe.posting_date, pi.posting_date)
pr = self.create_payment_reconciliation(party_is_customer=False)
pr.default_advance_account = self.advance_payable_account
pr.from_payment_date = pe.posting_date
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 1)
self.assertEqual(len(pr.payments), 1)
invoices = [invoice.as_dict() for invoice in pr.invoices]
payments = [payment.as_dict() for payment in pr.payments]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.reconcile()
# Assert Ledger Entries
gl_entries = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": pe.name, "is_cancelled": 0, "posting_date": pe.posting_date},
)
self.assertEqual(len(gl_entries), 4)
pl_entries = frappe.db.get_all(
"Payment Ledger Entry",
filters={"voucher_no": pe.name, "delinked": 0, "posting_date": pe.posting_date},
)
self.assertEqual(len(pl_entries), 3)
def make_customer(customer_name, currency=None):
if not frappe.db.exists("Customer", customer_name):

View File

@ -1035,10 +1035,10 @@ class PurchaseInvoice(BuyingController):
if provisional_accounting_for_non_stock_items:
if item.purchase_receipt:
provisional_account, pr_qty, pr_base_rate = frappe.get_cached_value(
provisional_account, pr_qty, pr_base_rate, pr_rate = frappe.get_cached_value(
"Purchase Receipt Item",
item.pr_detail,
["provisional_expense_account", "qty", "base_rate"],
["provisional_expense_account", "qty", "base_rate", "rate"],
)
provisional_account = provisional_account or self.get_company_default(
"default_provisional_account"
@ -1072,7 +1072,10 @@ class PurchaseInvoice(BuyingController):
self.posting_date,
provisional_account,
reverse=1,
item_amount=(min(item.qty, pr_qty) * pr_base_rate),
item_amount=(
(min(item.qty, pr_qty) * pr_rate)
* purchase_receipt_doc.get("conversion_rate")
),
)
if not self.is_internal_transfer():

View File

@ -1,8 +1,4 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.require("assets/erpnext/js/purchase_trends_filters.js", function () {
frappe.query_reports["Purchase Invoice Trends"] = {
filters: erpnext.get_purchase_trends_filters(),
};
});
frappe.query_reports["Purchase Invoice Trends"] = $.extend({}, erpnext.purchase_trends_filters);

View File

@ -1,8 +1,4 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.require("assets/erpnext/js/sales_trends_filters.js", function () {
frappe.query_reports["Sales Invoice Trends"] = {
filters: erpnext.get_sales_trends_filters(),
};
});
frappe.query_reports["Sales Invoice Trends"] = $.extend({}, erpnext.sales_trends_filters);

View File

@ -1,8 +1,4 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.require("assets/erpnext/js/purchase_trends_filters.js", function () {
frappe.query_reports["Purchase Order Trends"] = {
filters: erpnext.get_purchase_trends_filters(),
};
});
frappe.query_reports["Purchase Order Trends"] = $.extend({}, erpnext.purchase_trends_filters);

View File

@ -46,6 +46,8 @@ class calculate_taxes_and_totals:
self.discount_amount_applied = False
self._calculate()
self.calculate_shipping_charges()
if self.doc.meta.get_field("discount_amount"):
self.set_discount_amount()
self.apply_discount_amount()
@ -57,7 +59,6 @@ class calculate_taxes_and_totals:
self.doc.rounding_adjustment = self.doc.base_rounding_adjustment = 0.0
self.set_rounded_total()
self.calculate_shipping_charges()
if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
self.calculate_total_advance()

View File

@ -37,6 +37,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
async calculate_taxes_and_totals(update_paid_amount) {
this.discount_amount_applied = false;
this._calculate_taxes_and_totals();
await this.calculate_shipping_charges();
this.calculate_discount_amount();
// # Update grand total as per cash and non trade discount
@ -48,7 +49,6 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
this.set_rounded_total();
}
await this.calculate_shipping_charges();
// Advance calculation applicable to Sales/Purchase Invoice
if (

View File

@ -34,5 +34,7 @@ import "./utils/sales_common.js";
import "./controllers/buying.js";
import "./utils/demo.js";
import "./financial_statements.js";
import "./sales_trends_filters.js";
import "./purchase_trends_filters.js";
// import { sum } from 'frappe/public/utils/util.js'

View File

@ -1,8 +1,8 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
erpnext.get_purchase_trends_filters = function () {
return [
erpnext.purchase_trends_filters = {
filters: [
{
fieldname: "company",
label: __("Company"),
@ -63,5 +63,5 @@ erpnext.get_purchase_trends_filters = function () {
options: ["", { value: "Item", label: __("Item") }, { value: "Supplier", label: __("Supplier") }],
default: "",
},
];
],
};

View File

@ -1,8 +1,8 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
erpnext.get_sales_trends_filters = function () {
return [
erpnext.sales_trends_filters = {
filters: [
{
fieldname: "period",
label: __("Period"),
@ -53,5 +53,5 @@ erpnext.get_sales_trends_filters = function () {
options: "Company",
default: frappe.defaults.get_user_default("Company"),
},
];
],
};

View File

@ -1,8 +1,4 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.require("assets/erpnext/js/sales_trends_filters.js", function () {
frappe.query_reports["Quotation Trends"] = {
filters: erpnext.get_sales_trends_filters(),
};
});
frappe.query_reports["Quotation Trends"] = $.extend({}, erpnext.sales_trends_filters);

View File

@ -1,8 +1,4 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.require("assets/erpnext/js/sales_trends_filters.js", function () {
frappe.query_reports["Sales Order Trends"] = {
filters: erpnext.get_sales_trends_filters(),
};
});
frappe.query_reports["Sales Order Trends"] = $.extend({}, erpnext.sales_trends_filters);

View File

@ -67,6 +67,7 @@
"default_finance_book",
"advance_payments_section",
"book_advance_payments_in_separate_party_account",
"reconcile_on_advance_payment_date",
"column_break_fwcf",
"default_advance_received_account",
"default_advance_paid_account",
@ -779,6 +780,14 @@
"fieldtype": "Tab Break",
"label": "Dashboard",
"show_dashboard": 1
},
{
"default": "0",
"depends_on": "eval: doc.book_advance_payments_in_separate_party_account",
"description": "If <b>Enabled</b> - Reconciliation happens on the <b>Advance Payment posting date</b><br>\nIf <b>Disabled</b> - Reconciliation happens on oldest of 2 Dates: <b>Invoice Date</b> or the <b>Advance Payment posting date</b><br>\n",
"fieldname": "reconcile_on_advance_payment_date",
"fieldtype": "Check",
"label": "Reconcile on Advance Payment Date"
}
],
"icon": "fa fa-building",
@ -786,7 +795,7 @@
"image_field": "company_logo",
"is_tree": 1,
"links": [],
"modified": "2024-04-23 12:38:33.173938",
"modified": "2024-05-16 12:39:54.694232",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",

View File

@ -85,6 +85,7 @@ class Company(NestedSet):
parent_company: DF.Link | None
payment_terms: DF.Link | None
phone_no: DF.Data | None
reconcile_on_advance_payment_date: DF.Check
registration_details: DF.Code | None
rgt: DF.Int
round_off_account: DF.Link | None

View File

@ -17,15 +17,11 @@ class DeprecatedSerialNoValuation:
if not serial_nos:
return
actual_qty = flt(self.sle.actual_qty)
stock_value_change = 0
if actual_qty < 0:
if not self.sle.is_cancelled:
outgoing_value = self.get_incoming_value_for_serial_nos(serial_nos)
stock_value_change = -1 * outgoing_value
if not self.sle.is_cancelled:
stock_value_change = self.get_incoming_value_for_serial_nos(serial_nos)
self.stock_value_change += stock_value_change
self.stock_value_change += flt(stock_value_change)
def get_filterd_serial_nos(self):
serial_nos = []
@ -141,7 +137,14 @@ class DeprecatedBatchNoValuation:
if not self.non_batchwise_balance_qty:
continue
self.batch_avg_rate[batch_no] = self.non_batchwise_balance_value / self.non_batchwise_balance_qty
if self.non_batchwise_balance_value == 0:
self.batch_avg_rate[batch_no] = 0.0
self.stock_value_differece[batch_no] = 0.0
else:
self.batch_avg_rate[batch_no] = (
self.non_batchwise_balance_value / self.non_batchwise_balance_qty
)
self.stock_value_differece[batch_no] = self.non_batchwise_balance_value
stock_value_change = self.batch_avg_rate[batch_no] * ledger.qty
self.stock_value_change += stock_value_change

View File

@ -1697,6 +1697,132 @@ class TestDeliveryNote(FrappeTestCase):
if row.serial_no:
self.assertEqual(row.serial_no, serial_no)
def test_delivery_note_legacy_serial_no_valuation(self):
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
frappe.flags.ignore_serial_batch_bundle_validation = True
sn_item = "Old Serial NO Item Valuation Test - 2"
make_item(
sn_item,
{
"has_serial_no": 1,
"serial_no_series": "SN-SOVOSN-.####",
"is_stock_item": 1,
},
)
serial_nos = [
"SN-SOVOSN-1234",
"SN-SOVOSN-2234",
]
for sn in serial_nos:
if not frappe.db.exists("Serial No", sn):
sn_doc = frappe.get_doc(
{
"doctype": "Serial No",
"item_code": sn_item,
"serial_no": sn,
}
)
sn_doc.insert()
warehouse = "_Test Warehouse - _TC"
company = frappe.db.get_value("Warehouse", warehouse, "company")
se_doc = make_stock_entry(
item_code=sn_item,
company=company,
target="_Test Warehouse - _TC",
qty=2,
basic_rate=150,
do_not_submit=1,
use_serial_batch_fields=0,
)
se_doc.submit()
se_doc.items[0].db_set("serial_no", "\n".join(serial_nos))
sle_data = frappe.get_all(
"Stock Ledger Entry",
filters={"voucher_no": se_doc.name, "voucher_type": "Stock Entry"},
)[0]
sle_doc = frappe.get_doc("Stock Ledger Entry", sle_data.name)
self.assertFalse(sle_doc.serial_no)
sle_doc.db_set("serial_no", "\n".join(serial_nos))
sle_doc.reload()
self.assertTrue(sle_doc.serial_no)
self.assertFalse(sle_doc.is_cancelled)
for sn in serial_nos:
sn_doc = frappe.get_doc("Serial No", sn)
sn_doc.db_set(
{
"status": "Active",
"warehouse": warehouse,
}
)
self.assertEqual(sorted(get_serial_nos(se_doc.items[0].serial_no)), sorted(serial_nos))
frappe.flags.ignore_serial_batch_bundle_validation = False
se_doc = make_stock_entry(
item_code=sn_item,
company=company,
target="_Test Warehouse - _TC",
qty=2,
basic_rate=200,
)
serial_nos.extend(get_serial_nos_from_bundle(se_doc.items[0].serial_and_batch_bundle))
dn = create_delivery_note(
item_code=sn_item,
qty=3,
rate=500,
warehouse=warehouse,
company=company,
expense_account="Cost of Goods Sold - _TC",
cost_center="Main - _TC",
use_serial_batch_fields=1,
serial_no="\n".join(serial_nos[0:3]),
)
dn.reload()
sle_data = frappe.get_all(
"Stock Ledger Entry",
filters={"voucher_no": dn.name, "voucher_type": "Delivery Note"},
fields=["stock_value_difference", "actual_qty"],
)[0]
self.assertEqual(sle_data.actual_qty, 3 * -1)
self.assertEqual(sle_data.stock_value_difference, 500.0 * -1)
dn = create_delivery_note(
item_code=sn_item,
qty=1,
rate=500,
warehouse=warehouse,
company=company,
expense_account="Cost of Goods Sold - _TC",
cost_center="Main - _TC",
use_serial_batch_fields=1,
serial_no=serial_nos[-1],
)
dn.reload()
sle_data = frappe.get_all(
"Stock Ledger Entry",
filters={"voucher_no": dn.name, "voucher_type": "Delivery Note"},
fields=["stock_value_difference", "actual_qty"],
)[0]
self.assertEqual(sle_data.actual_qty, 1 * -1)
self.assertEqual(sle_data.stock_value_difference, 200.0 * -1)
def create_delivery_note(**args):
dn = frappe.new_doc("Delivery Note")

View File

@ -1,8 +1,4 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.require("assets/erpnext/js/sales_trends_filters.js", function () {
frappe.query_reports["Delivery Note Trends"] = {
filters: erpnext.get_sales_trends_filters(),
};
});
frappe.query_reports["Delivery Note Trends"] = $.extend({}, erpnext.sales_trends_filters);

View File

@ -1,8 +1,4 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.require("assets/erpnext/js/purchase_trends_filters.js", function () {
frappe.query_reports["Purchase Receipt Trends"] = {
filters: erpnext.get_purchase_trends_filters(),
};
});
frappe.query_reports["Purchase Receipt Trends"] = $.extend({}, erpnext.purchase_trends_filters);

View File

@ -553,7 +553,7 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
self.set_stock_value_difference()
def get_batch_no_ledgers(self) -> list[dict]:
if not self.batches:
if not self.batchwise_valuation_batches:
return []
parent = frappe.qb.DocType("Serial and Batch Bundle")
@ -575,7 +575,7 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
Sum(child.qty).as_("qty"),
)
.where(
(child.batch_no.isin(self.batches))
(child.batch_no.isin(self.batchwise_valuation_batches))
& (parent.warehouse == self.sle.warehouse)
& (parent.item_code == self.sle.item_code)
& (parent.docstatus == 1)