mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-02 13:08:27 +00:00
Merge branch 'version-14-hotfix' into version-14
This commit is contained in:
@@ -51,7 +51,7 @@ frappe.ui.form.on("Journal Entry", {
|
||||
}, __('Make'));
|
||||
}
|
||||
|
||||
erpnext.accounts.unreconcile_payments.add_unreconcile_btn(frm);
|
||||
erpnext.accounts.unreconcile_payment.add_unreconcile_btn(frm);
|
||||
},
|
||||
before_save: function(frm) {
|
||||
if ((frm.doc.docstatus == 0) && (!frm.doc.is_system_generated)) {
|
||||
|
||||
@@ -548,8 +548,16 @@
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 176,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-08-10 14:32:22.366895",
|
||||
"links": [
|
||||
{
|
||||
"is_child_table": 1,
|
||||
"link_doctype": "Bank Transaction Payments",
|
||||
"link_fieldname": "payment_entry",
|
||||
"parent_doctype": "Bank Transaction",
|
||||
"table_fieldname": "payment_entries"
|
||||
}
|
||||
],
|
||||
"modified": "2023-11-23 12:11:04.128015",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Journal Entry",
|
||||
|
||||
@@ -203,7 +203,8 @@
|
||||
"fieldtype": "Select",
|
||||
"label": "Reference Type",
|
||||
"no_copy": 1,
|
||||
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement\nPayment Entry"
|
||||
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement\nPayment Entry",
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "reference_name",
|
||||
@@ -211,7 +212,8 @@
|
||||
"in_list_view": 1,
|
||||
"label": "Reference Name",
|
||||
"no_copy": 1,
|
||||
"options": "reference_type"
|
||||
"options": "reference_type",
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance'])",
|
||||
@@ -278,13 +280,14 @@
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Reference Detail No",
|
||||
"no_copy": 1
|
||||
"no_copy": 1,
|
||||
"search_index": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-06-16 14:11:13.507807",
|
||||
"modified": "2023-11-23 11:44:25.841187",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Journal Entry Account",
|
||||
|
||||
@@ -7,7 +7,7 @@ cur_frm.cscript.tax_table = "Advance Taxes and Charges";
|
||||
|
||||
frappe.ui.form.on('Payment Entry', {
|
||||
onload: function(frm) {
|
||||
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', 'Repost Payment Ledger','Repost Accounting Ledger', 'Unreconcile Payments', 'Unreconcile Payment Entries'];
|
||||
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', 'Repost Payment Ledger','Repost Accounting Ledger', 'Unreconcile Payment', 'Unreconcile Payment Entries'];
|
||||
|
||||
if(frm.doc.__islocal) {
|
||||
if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null);
|
||||
@@ -158,7 +158,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
}, __('Actions'));
|
||||
|
||||
}
|
||||
erpnext.accounts.unreconcile_payments.add_unreconcile_btn(frm);
|
||||
erpnext.accounts.unreconcile_payment.add_unreconcile_btn(frm);
|
||||
},
|
||||
|
||||
validate_company: (frm) => {
|
||||
|
||||
@@ -739,8 +739,16 @@
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-06-19 11:38:04.387219",
|
||||
"links": [
|
||||
{
|
||||
"is_child_table": 1,
|
||||
"link_doctype": "Bank Transaction Payments",
|
||||
"link_fieldname": "payment_entry",
|
||||
"parent_doctype": "Bank Transaction",
|
||||
"table_fieldname": "payment_entries"
|
||||
}
|
||||
],
|
||||
"modified": "2023-11-23 12:07:20.887885",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Entry",
|
||||
@@ -786,4 +794,4 @@
|
||||
"states": [],
|
||||
"title_field": "title",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ class PaymentEntry(AccountsController):
|
||||
"Repost Payment Ledger Items",
|
||||
"Repost Accounting Ledger",
|
||||
"Repost Accounting Ledger Items",
|
||||
"Unreconcile Payments",
|
||||
"Unreconcile Payment",
|
||||
"Unreconcile Payment Entries",
|
||||
)
|
||||
super(PaymentEntry, self).on_cancel()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
@@ -21,6 +22,7 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import (
|
||||
create_sales_invoice,
|
||||
create_sales_invoice_against_cost_center,
|
||||
)
|
||||
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||
from erpnext.setup.doctype.employee.test_employee import make_employee
|
||||
|
||||
@@ -1219,6 +1221,76 @@ class TestPaymentEntry(FrappeTestCase):
|
||||
so.reload()
|
||||
self.assertEqual(so.advance_paid, so.rounded_total)
|
||||
|
||||
def test_partial_cancel_for_payment_entry(self):
|
||||
si = create_sales_invoice()
|
||||
|
||||
pe = get_payment_entry(si.doctype, si.name)
|
||||
pe.save()
|
||||
pe.submit()
|
||||
|
||||
# Additional GL Entry
|
||||
tax_amount = 10
|
||||
reference_row = pe.references[0]
|
||||
gl_args = {
|
||||
"party_type": pe.party_type,
|
||||
"party": pe.party,
|
||||
"against_voucher_type": reference_row.reference_doctype,
|
||||
"against_voucher": reference_row.reference_name,
|
||||
"voucher_detail_no": reference_row.name,
|
||||
}
|
||||
|
||||
gl_dicts = []
|
||||
|
||||
gl_dicts.extend(
|
||||
[
|
||||
pe.get_gl_dict(
|
||||
{
|
||||
"account": pe.paid_to,
|
||||
"credit": tax_amount,
|
||||
"credit_in_account_currency": tax_amount,
|
||||
**gl_args,
|
||||
}
|
||||
),
|
||||
pe.get_gl_dict(
|
||||
{
|
||||
"account": pe.paid_from,
|
||||
"debit": tax_amount,
|
||||
"debit_in_account_currency": tax_amount,
|
||||
**gl_args,
|
||||
}
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
make_gl_entries(gl_dicts)
|
||||
|
||||
# Assert PLEs Before
|
||||
self.assertPLEntries(
|
||||
pe,
|
||||
[
|
||||
{"amount": -100.0, "against_voucher_no": si.name},
|
||||
{"amount": 10.0, "against_voucher_no": si.name},
|
||||
],
|
||||
)
|
||||
|
||||
# Partially cancel Payment Entry
|
||||
make_reverse_gl_entries(gl_dicts, partial_cancel=True)
|
||||
self.assertPLEntries(pe, [{"amount": -100.0, "against_voucher_no": si.name}])
|
||||
|
||||
def assertPLEntries(self, payment_doc, expected_pl_entries):
|
||||
pl_entries = frappe.get_all(
|
||||
"Payment Ledger Entry",
|
||||
filters={
|
||||
"voucher_type": payment_doc.doctype,
|
||||
"voucher_no": payment_doc.name,
|
||||
"delinked": 0,
|
||||
},
|
||||
fields=["amount", "against_voucher_no"],
|
||||
)
|
||||
out_str = json.dumps(sorted(pl_entries, key=json.dumps))
|
||||
expected_out_str = json.dumps(sorted(expected_pl_entries, key=json.dumps))
|
||||
self.assertEqual(out_str, expected_out_str)
|
||||
|
||||
|
||||
def create_payment_entry(**args):
|
||||
payment_entry = frappe.new_doc("Payment Entry")
|
||||
|
||||
@@ -181,7 +181,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
|
||||
}
|
||||
|
||||
this.frm.set_df_property("tax_withholding_category", "hidden", doc.apply_tds ? 0 : 1);
|
||||
erpnext.accounts.unreconcile_payments.add_unreconcile_btn(me.frm);
|
||||
erpnext.accounts.unreconcile_payment.add_unreconcile_btn(me.frm);
|
||||
}
|
||||
|
||||
unblock_invoice() {
|
||||
|
||||
@@ -532,7 +532,11 @@ class PurchaseInvoice(BuyingController):
|
||||
if self.update_stock == 1:
|
||||
self.repost_future_sle_and_gle()
|
||||
|
||||
self.update_project()
|
||||
if (
|
||||
frappe.db.get_single_value("Buying Settings", "project_update_frequency") == "Each Transaction"
|
||||
):
|
||||
self.update_project()
|
||||
|
||||
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
|
||||
self.update_advance_tax_references()
|
||||
|
||||
@@ -1269,7 +1273,10 @@ class PurchaseInvoice(BuyingController):
|
||||
if self.update_stock == 1:
|
||||
self.repost_future_sle_and_gle()
|
||||
|
||||
self.update_project()
|
||||
if (
|
||||
frappe.db.get_single_value("Buying Settings", "project_update_frequency") == "Each Transaction"
|
||||
):
|
||||
self.update_project()
|
||||
self.db_set("status", "Cancelled")
|
||||
|
||||
unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference)
|
||||
@@ -1287,13 +1294,21 @@ class PurchaseInvoice(BuyingController):
|
||||
self.update_advance_tax_references(cancel=1)
|
||||
|
||||
def update_project(self):
|
||||
project_list = []
|
||||
projects = frappe._dict()
|
||||
for d in self.items:
|
||||
if d.project and d.project not in project_list:
|
||||
project = frappe.get_doc("Project", d.project)
|
||||
project.update_purchase_costing()
|
||||
project.db_update()
|
||||
project_list.append(d.project)
|
||||
if d.project:
|
||||
if self.docstatus == 1:
|
||||
projects[d.project] = projects.get(d.project, 0) + d.base_net_amount
|
||||
elif self.docstatus == 2:
|
||||
projects[d.project] = projects.get(d.project, 0) - d.base_net_amount
|
||||
|
||||
pj = frappe.qb.DocType("Project")
|
||||
for proj, value in projects.items():
|
||||
res = (
|
||||
frappe.qb.from_(pj).select(pj.total_purchase_cost).where(pj.name == proj).for_update().run()
|
||||
)
|
||||
current_purchase_cost = res and res[0][0] or 0
|
||||
frappe.db.set_value("Project", proj, "total_purchase_cost", current_purchase_cost + value)
|
||||
|
||||
def validate_supplier_invoice(self):
|
||||
if self.bill_date:
|
||||
|
||||
@@ -34,7 +34,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
|
||||
super.onload();
|
||||
|
||||
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet', 'POS Invoice Merge Log',
|
||||
'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payments", "Unreconcile Payment Entries"];
|
||||
'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries"];
|
||||
|
||||
if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
|
||||
// show debit_to in print format
|
||||
@@ -178,7 +178,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
|
||||
}
|
||||
}
|
||||
|
||||
erpnext.accounts.unreconcile_payments.add_unreconcile_btn(me.frm);
|
||||
erpnext.accounts.unreconcile_payment.add_unreconcile_btn(me.frm);
|
||||
}
|
||||
|
||||
make_maintenance_schedule() {
|
||||
|
||||
@@ -1613,7 +1613,8 @@
|
||||
"hide_seconds": 1,
|
||||
"label": "Inter Company Invoice Reference",
|
||||
"options": "Purchase Invoice",
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "customer_group",
|
||||
@@ -2164,7 +2165,7 @@
|
||||
"link_fieldname": "consolidated_invoice"
|
||||
}
|
||||
],
|
||||
"modified": "2023-11-20 11:51:43.555197",
|
||||
"modified": "2023-11-23 16:56:29.679499",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
||||
@@ -408,7 +408,7 @@ class SalesInvoice(SellingController):
|
||||
"Repost Payment Ledger Items",
|
||||
"Repost Accounting Ledger",
|
||||
"Repost Accounting Ledger Items",
|
||||
"Unreconcile Payments",
|
||||
"Unreconcile Payment",
|
||||
"Unreconcile Payment Entries",
|
||||
"Payment Ledger Entry",
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal
|
||||
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
|
||||
|
||||
|
||||
class TestUnreconcilePayments(AccountsTestMixin, FrappeTestCase):
|
||||
class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
|
||||
def setUp(self):
|
||||
self.create_company()
|
||||
self.create_customer()
|
||||
@@ -73,7 +73,7 @@ class TestUnreconcilePayments(AccountsTestMixin, FrappeTestCase):
|
||||
|
||||
unreconcile = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Unreconcile Payments",
|
||||
"doctype": "Unreconcile Payment",
|
||||
"company": self.company,
|
||||
"voucher_type": pe.doctype,
|
||||
"voucher_no": pe.name,
|
||||
@@ -138,7 +138,7 @@ class TestUnreconcilePayments(AccountsTestMixin, FrappeTestCase):
|
||||
|
||||
unreconcile = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Unreconcile Payments",
|
||||
"doctype": "Unreconcile Payment",
|
||||
"company": self.company,
|
||||
"voucher_type": pe2.doctype,
|
||||
"voucher_no": pe2.name,
|
||||
@@ -196,7 +196,7 @@ class TestUnreconcilePayments(AccountsTestMixin, FrappeTestCase):
|
||||
|
||||
unreconcile = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Unreconcile Payments",
|
||||
"doctype": "Unreconcile Payment",
|
||||
"company": self.company,
|
||||
"voucher_type": pe.doctype,
|
||||
"voucher_no": pe.name,
|
||||
@@ -281,7 +281,7 @@ class TestUnreconcilePayments(AccountsTestMixin, FrappeTestCase):
|
||||
|
||||
unreconcile = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Unreconcile Payments",
|
||||
"doctype": "Unreconcile Payment",
|
||||
"company": self.company,
|
||||
"voucher_type": pe2.doctype,
|
||||
"voucher_no": pe2.name,
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Unreconcile Payments", {
|
||||
frappe.ui.form.on("Unreconcile Payment", {
|
||||
refresh(frm) {
|
||||
frm.set_query("voucher_type", function() {
|
||||
return {
|
||||
@@ -21,7 +21,7 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Amended From",
|
||||
"no_copy": 1,
|
||||
"options": "Unreconcile Payments",
|
||||
"options": "Unreconcile Payment",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -61,7 +61,7 @@
|
||||
"modified": "2023-08-28 17:42:50.261377",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Unreconcile Payments",
|
||||
"name": "Unreconcile Payment",
|
||||
"naming_rule": "Expression",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
@@ -90,4 +90,4 @@
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ from erpnext.accounts.utils import (
|
||||
)
|
||||
|
||||
|
||||
class UnreconcilePayments(Document):
|
||||
class UnreconcilePayment(Document):
|
||||
def validate(self):
|
||||
self.supported_types = ["Payment Entry", "Journal Entry"]
|
||||
if not self.voucher_type in self.supported_types:
|
||||
@@ -142,7 +142,7 @@ def create_unreconcile_doc_for_selection(selections=None):
|
||||
selections = frappe.json.loads(selections)
|
||||
# assuming each row is a unique voucher
|
||||
for row in selections:
|
||||
unrecon = frappe.new_doc("Unreconcile Payments")
|
||||
unrecon = frappe.new_doc("Unreconcile Payment")
|
||||
unrecon.company = row.get("company")
|
||||
unrecon.voucher_type = row.get("voucher_type")
|
||||
unrecon.voucher_no = row.get("voucher_no")
|
||||
@@ -556,7 +556,12 @@ def get_round_off_account_and_cost_center(
|
||||
|
||||
|
||||
def make_reverse_gl_entries(
|
||||
gl_entries=None, voucher_type=None, voucher_no=None, adv_adj=False, update_outstanding="Yes"
|
||||
gl_entries=None,
|
||||
voucher_type=None,
|
||||
voucher_no=None,
|
||||
adv_adj=False,
|
||||
update_outstanding="Yes",
|
||||
partial_cancel=False,
|
||||
):
|
||||
"""
|
||||
Get original gl entries of the voucher
|
||||
@@ -576,14 +581,19 @@ def make_reverse_gl_entries(
|
||||
|
||||
if gl_entries:
|
||||
create_payment_ledger_entry(
|
||||
gl_entries, cancel=1, adv_adj=adv_adj, update_outstanding=update_outstanding
|
||||
gl_entries,
|
||||
cancel=1,
|
||||
adv_adj=adv_adj,
|
||||
update_outstanding=update_outstanding,
|
||||
partial_cancel=partial_cancel,
|
||||
)
|
||||
validate_accounting_period(gl_entries)
|
||||
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
|
||||
|
||||
is_opening = any(d.get("is_opening") == "Yes" for d in gl_entries)
|
||||
validate_against_pcv(is_opening, gl_entries[0]["posting_date"], gl_entries[0]["company"])
|
||||
set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"])
|
||||
if not partial_cancel:
|
||||
set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"])
|
||||
|
||||
for entry in gl_entries:
|
||||
new_gle = copy.deepcopy(entry)
|
||||
|
||||
@@ -1617,6 +1617,7 @@ def get_payment_ledger_entries(gl_entries, cancel=0):
|
||||
due_date=gle.due_date,
|
||||
voucher_type=gle.voucher_type,
|
||||
voucher_no=gle.voucher_no,
|
||||
voucher_detail_no=gle.voucher_detail_no,
|
||||
against_voucher_type=gle.against_voucher_type
|
||||
if gle.against_voucher_type
|
||||
else gle.voucher_type,
|
||||
@@ -1638,7 +1639,7 @@ def get_payment_ledger_entries(gl_entries, cancel=0):
|
||||
|
||||
|
||||
def create_payment_ledger_entry(
|
||||
gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0
|
||||
gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0, partial_cancel=False
|
||||
):
|
||||
if gl_entries:
|
||||
ple_map = get_payment_ledger_entries(gl_entries, cancel=cancel)
|
||||
@@ -1648,7 +1649,7 @@ def create_payment_ledger_entry(
|
||||
ple = frappe.get_doc(entry)
|
||||
|
||||
if cancel:
|
||||
delink_original_entry(ple)
|
||||
delink_original_entry(ple, partial_cancel=partial_cancel)
|
||||
|
||||
ple.flags.ignore_permissions = 1
|
||||
ple.flags.adv_adj = adv_adj
|
||||
@@ -1695,7 +1696,7 @@ def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, pa
|
||||
ref_doc.set_status(update=True)
|
||||
|
||||
|
||||
def delink_original_entry(pl_entry):
|
||||
def delink_original_entry(pl_entry, partial_cancel=False):
|
||||
if pl_entry:
|
||||
ple = qb.DocType("Payment Ledger Entry")
|
||||
query = (
|
||||
@@ -1715,6 +1716,10 @@ def delink_original_entry(pl_entry):
|
||||
& (ple.against_voucher_no == pl_entry.against_voucher_no)
|
||||
)
|
||||
)
|
||||
|
||||
if partial_cancel:
|
||||
query = query.where(ple.voucher_detail_no == pl_entry.voucher_detail_no)
|
||||
|
||||
query.run()
|
||||
|
||||
|
||||
@@ -1791,6 +1796,28 @@ class QueryPaymentLedger(object):
|
||||
Table("outstanding").amount_in_account_currency >= self.max_outstanding
|
||||
)
|
||||
|
||||
if self.limit and self.get_invoices:
|
||||
outstanding_vouchers = (
|
||||
qb.from_(ple)
|
||||
.select(
|
||||
ple.against_voucher_no.as_("voucher_no"),
|
||||
Sum(ple.amount_in_account_currency).as_("amount_in_account_currency"),
|
||||
)
|
||||
.where(ple.delinked == 0)
|
||||
.where(Criterion.all(filter_on_against_voucher_no))
|
||||
.where(Criterion.all(self.common_filter))
|
||||
.groupby(ple.against_voucher_type, ple.against_voucher_no, ple.party_type, ple.party)
|
||||
.orderby(ple.posting_date, ple.voucher_no)
|
||||
.having(qb.Field("amount_in_account_currency") > 0)
|
||||
.limit(self.limit)
|
||||
.run()
|
||||
)
|
||||
if outstanding_vouchers:
|
||||
filter_on_voucher_no.append(ple.voucher_no.isin([x[0] for x in outstanding_vouchers]))
|
||||
filter_on_against_voucher_no.append(
|
||||
ple.against_voucher_no.isin([x[0] for x in outstanding_vouchers])
|
||||
)
|
||||
|
||||
# build query for voucher amount
|
||||
query_voucher_amount = (
|
||||
qb.from_(ple)
|
||||
|
||||
@@ -464,6 +464,9 @@ def restore_asset(asset_name):
|
||||
|
||||
|
||||
def depreciate_asset(asset, date):
|
||||
if not asset.calculate_depreciation:
|
||||
return
|
||||
|
||||
asset.flags.ignore_validate_update_after_submit = True
|
||||
asset.prepare_depreciation_data(date_of_disposal=date)
|
||||
asset.save()
|
||||
@@ -472,6 +475,9 @@ def depreciate_asset(asset, date):
|
||||
|
||||
|
||||
def reset_depreciation_schedule(asset, date):
|
||||
if not asset.calculate_depreciation:
|
||||
return
|
||||
|
||||
asset.flags.ignore_validate_update_after_submit = True
|
||||
|
||||
# recreate original depreciation schedule of the asset
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"po_required",
|
||||
"pr_required",
|
||||
"blanket_order_allowance",
|
||||
"project_update_frequency",
|
||||
"column_break_12",
|
||||
"maintain_same_rate",
|
||||
"set_landed_cost_based_on_purchase_invoice_rate",
|
||||
@@ -172,6 +173,14 @@
|
||||
"fieldname": "blanket_order_allowance",
|
||||
"fieldtype": "Float",
|
||||
"label": "Blanket Order Allowance (%)"
|
||||
},
|
||||
{
|
||||
"default": "Each Transaction",
|
||||
"description": "How often should Project be updated of Total Purchase Cost ?",
|
||||
"fieldname": "project_update_frequency",
|
||||
"fieldtype": "Select",
|
||||
"label": "Update frequency of Project",
|
||||
"options": "Each Transaction\nManual"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-cog",
|
||||
@@ -179,7 +188,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2023-10-25 14:03:32.520418",
|
||||
"modified": "2023-11-24 10:55:51.287327",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Buying Settings",
|
||||
|
||||
@@ -143,16 +143,17 @@ class Supplier(TransactionBase):
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_supplier_primary_contact(doctype, txt, searchfield, start, page_len, filters):
|
||||
supplier = filters.get("supplier")
|
||||
return frappe.db.sql(
|
||||
"""
|
||||
SELECT
|
||||
`tabContact`.name from `tabContact`,
|
||||
`tabDynamic Link`
|
||||
WHERE
|
||||
`tabContact`.name = `tabDynamic Link`.parent
|
||||
and `tabDynamic Link`.link_name = %(supplier)s
|
||||
and `tabDynamic Link`.link_doctype = 'Supplier'
|
||||
and `tabContact`.name like %(txt)s
|
||||
""",
|
||||
{"supplier": supplier, "txt": "%%%s%%" % txt},
|
||||
)
|
||||
contact = frappe.qb.DocType("Contact")
|
||||
dynamic_link = frappe.qb.DocType("Dynamic Link")
|
||||
|
||||
return (
|
||||
frappe.qb.from_(contact)
|
||||
.join(dynamic_link)
|
||||
.on(contact.name == dynamic_link.parent)
|
||||
.select(contact.name, contact.email_id)
|
||||
.where(
|
||||
(dynamic_link.link_name == supplier)
|
||||
& (dynamic_link.link_doctype == "Supplier")
|
||||
& (contact.name.like("%{0}%".format(txt)))
|
||||
)
|
||||
).run(as_dict=False)
|
||||
|
||||
@@ -238,7 +238,7 @@ class AccountsController(TransactionBase):
|
||||
references_map.setdefault(x.parent, []).append(x.name)
|
||||
|
||||
for doc, rows in references_map.items():
|
||||
unreconcile_doc = frappe.get_doc("Unreconcile Payments", doc)
|
||||
unreconcile_doc = frappe.get_doc("Unreconcile Payment", doc)
|
||||
for row in rows:
|
||||
unreconcile_doc.remove(unreconcile_doc.get("allocations", {"name": row})[0])
|
||||
|
||||
@@ -247,9 +247,9 @@ class AccountsController(TransactionBase):
|
||||
unreconcile_doc.save(ignore_permissions=True)
|
||||
|
||||
# delete docs upon parent doc deletion
|
||||
unreconcile_docs = frappe.db.get_all("Unreconcile Payments", filters={"voucher_no": self.name})
|
||||
unreconcile_docs = frappe.db.get_all("Unreconcile Payment", filters={"voucher_no": self.name})
|
||||
for x in unreconcile_docs:
|
||||
_doc = frappe.get_doc("Unreconcile Payments", x.name)
|
||||
_doc = frappe.get_doc("Unreconcile Payment", x.name)
|
||||
if _doc.docstatus == 1:
|
||||
_doc.cancel()
|
||||
_doc.delete()
|
||||
|
||||
@@ -351,5 +351,6 @@ erpnext.patches.v14_0.rename_depreciation_amount_based_on_num_days_in_month_to_d
|
||||
erpnext.patches.v14_0.add_default_for_repost_settings
|
||||
erpnext.patches.v14_0.create_accounting_dimensions_in_supplier_quotation
|
||||
erpnext.patches.v14_0.update_zero_asset_quantity_field
|
||||
execute:frappe.db.set_single_value("Buying Settings", "project_update_frequency", "Each Transaction")
|
||||
# below migration patch should always run last
|
||||
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
|
||||
|
||||
@@ -21,6 +21,9 @@ def execute():
|
||||
params = set({x.casefold(): x for x in params}.values())
|
||||
|
||||
for parameter in params:
|
||||
if frappe.db.exists("Quality Inspection Parameter", parameter):
|
||||
continue
|
||||
|
||||
frappe.get_doc(
|
||||
{"doctype": "Quality Inspection Parameter", "parameter": parameter, "description": parameter}
|
||||
).insert(ignore_permissions=True)
|
||||
|
||||
@@ -68,6 +68,10 @@ frappe.ui.form.on("Project", {
|
||||
frm.events.create_duplicate(frm);
|
||||
}, __("Actions"));
|
||||
|
||||
frm.add_custom_button(__('Update Total Purchase Cost'), () => {
|
||||
frm.events.update_total_purchase_cost(frm);
|
||||
}, __("Actions"));
|
||||
|
||||
frm.trigger("set_project_status_button");
|
||||
|
||||
|
||||
@@ -92,6 +96,22 @@ frappe.ui.form.on("Project", {
|
||||
|
||||
},
|
||||
|
||||
update_total_purchase_cost: function(frm) {
|
||||
frappe.call({
|
||||
method: "erpnext.projects.doctype.project.project.recalculate_project_total_purchase_cost",
|
||||
args: {project: frm.doc.name},
|
||||
freeze: true,
|
||||
freeze_message: __('Recalculating Purchase Cost against this Project...'),
|
||||
callback: function(r) {
|
||||
if (r && !r.exc) {
|
||||
frappe.msgprint(__('Total Purchase Cost has been updated'));
|
||||
frm.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
set_project_status_button: function(frm) {
|
||||
frm.add_custom_button(__('Set Project Status'), () => {
|
||||
let d = new frappe.ui.Dialog({
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
|
||||
import frappe
|
||||
from email_reply_parser import EmailReplyParser
|
||||
from frappe import _
|
||||
from frappe import _, qb
|
||||
from frappe.desk.reportview import get_match_cond
|
||||
from frappe.model.document import Document
|
||||
from frappe.query_builder.functions import Sum
|
||||
from frappe.utils import add_days, flt, get_datetime, get_time, get_url, nowtime, today
|
||||
|
||||
from erpnext import get_default_company
|
||||
@@ -246,12 +247,7 @@ class Project(Document):
|
||||
self.per_gross_margin = (self.gross_margin / flt(self.total_billed_amount)) * 100
|
||||
|
||||
def update_purchase_costing(self):
|
||||
total_purchase_cost = frappe.db.sql(
|
||||
"""select sum(base_net_amount)
|
||||
from `tabPurchase Invoice Item` where project = %s and docstatus=1""",
|
||||
self.name,
|
||||
)
|
||||
|
||||
total_purchase_cost = calculate_total_purchase_cost(self.name)
|
||||
self.total_purchase_cost = total_purchase_cost and total_purchase_cost[0][0] or 0
|
||||
|
||||
def update_sales_amount(self):
|
||||
@@ -671,3 +667,29 @@ def get_holiday_list(company=None):
|
||||
|
||||
def get_users_email(doc):
|
||||
return [d.email for d in doc.users if frappe.db.get_value("User", d.user, "enabled")]
|
||||
|
||||
|
||||
def calculate_total_purchase_cost(project: str | None = None):
|
||||
if project:
|
||||
pitem = qb.DocType("Purchase Invoice Item")
|
||||
frappe.qb.DocType("Purchase Invoice Item")
|
||||
total_purchase_cost = (
|
||||
qb.from_(pitem)
|
||||
.select(Sum(pitem.base_net_amount))
|
||||
.where((pitem.project == project) & (pitem.docstatus == 1))
|
||||
.run(as_list=True)
|
||||
)
|
||||
return total_purchase_cost
|
||||
return None
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def recalculate_project_total_purchase_cost(project: str | None = None):
|
||||
if project:
|
||||
total_purchase_cost = calculate_total_purchase_cost(project)
|
||||
frappe.db.set_value(
|
||||
"Project",
|
||||
project,
|
||||
"total_purchase_cost",
|
||||
(total_purchase_cost and total_purchase_cost[0][0] or 0),
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
frappe.provide('erpnext.accounts');
|
||||
|
||||
erpnext.accounts.unreconcile_payments = {
|
||||
erpnext.accounts.unreconcile_payment = {
|
||||
add_unreconcile_btn(frm) {
|
||||
if (frm.doc.docstatus == 1) {
|
||||
if(((frm.doc.doctype == "Journal Entry") && (frm.doc.voucher_type != "Journal Entry"))
|
||||
@@ -10,7 +10,7 @@ erpnext.accounts.unreconcile_payments = {
|
||||
}
|
||||
|
||||
frappe.call({
|
||||
"method": "erpnext.accounts.doctype.unreconcile_payments.unreconcile_payments.doc_has_references",
|
||||
"method": "erpnext.accounts.doctype.unreconcile_payment.unreconcile_payment.doc_has_references",
|
||||
"args": {
|
||||
"doctype": frm.doc.doctype,
|
||||
"docname": frm.doc.name
|
||||
@@ -18,7 +18,7 @@ erpnext.accounts.unreconcile_payments = {
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
frm.add_custom_button(__("UnReconcile"), function() {
|
||||
erpnext.accounts.unreconcile_payments.build_unreconcile_dialog(frm);
|
||||
erpnext.accounts.unreconcile_payment.build_unreconcile_dialog(frm);
|
||||
}, __('Actions'));
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ erpnext.accounts.unreconcile_payments = {
|
||||
|
||||
// get linked payments
|
||||
frappe.call({
|
||||
"method": "erpnext.accounts.doctype.unreconcile_payments.unreconcile_payments.get_linked_payments_for_doc",
|
||||
"method": "erpnext.accounts.doctype.unreconcile_payment.unreconcile_payment.get_linked_payments_for_doc",
|
||||
"args": {
|
||||
"company": frm.doc.company,
|
||||
"doctype": frm.doc.doctype,
|
||||
@@ -96,8 +96,8 @@ erpnext.accounts.unreconcile_payments = {
|
||||
|
||||
let selected_allocations = values.allocations.filter(x=>x.__checked);
|
||||
if (selected_allocations.length > 0) {
|
||||
let selection_map = erpnext.accounts.unreconcile_payments.build_selection_map(frm, selected_allocations);
|
||||
erpnext.accounts.unreconcile_payments.create_unreconcile_docs(selection_map);
|
||||
let selection_map = erpnext.accounts.unreconcile_payment.build_selection_map(frm, selected_allocations);
|
||||
erpnext.accounts.unreconcile_payment.create_unreconcile_docs(selection_map);
|
||||
d.hide();
|
||||
|
||||
} else {
|
||||
@@ -115,7 +115,7 @@ erpnext.accounts.unreconcile_payments = {
|
||||
|
||||
create_unreconcile_docs(selection_map) {
|
||||
frappe.call({
|
||||
"method": "erpnext.accounts.doctype.unreconcile_payments.unreconcile_payments.create_unreconcile_doc_for_selection",
|
||||
"method": "erpnext.accounts.doctype.unreconcile_payment.unreconcile_payment.create_unreconcile_doc_for_selection",
|
||||
"args": {
|
||||
"selections": selection_map
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user