mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-15 01:43:10 +00:00
Merge pull request #56057 from mihir-kandoi/pg-accounts-payments
This commit is contained in:
@@ -5,6 +5,8 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.docstatus import DocStatus
|
from frappe.model.docstatus import DocStatus
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe.query_builder import Tuple
|
||||||
|
from frappe.query_builder.functions import Abs, Max, Sum
|
||||||
from frappe.utils import flt, getdate
|
from frappe.utils import flt, getdate
|
||||||
|
|
||||||
|
|
||||||
@@ -478,30 +480,28 @@ def get_clearance_details(transaction, payment_entry, bt_allocations, gl_entries
|
|||||||
|
|
||||||
|
|
||||||
def get_related_bank_gl_entries(docs):
|
def get_related_bank_gl_entries(docs):
|
||||||
# nosemgrep: frappe-semgrep-rules.rules.frappe-using-db-sql
|
|
||||||
if not docs:
|
if not docs:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
result = frappe.db.sql(
|
gle = frappe.qb.DocType("GL Entry")
|
||||||
"""
|
ac = frappe.qb.DocType("Account")
|
||||||
SELECT
|
result = (
|
||||||
gle.voucher_type AS doctype,
|
frappe.qb.from_(gle)
|
||||||
gle.voucher_no AS docname,
|
.left_join(ac)
|
||||||
gle.account AS gl_account,
|
.on(ac.name == gle.account)
|
||||||
SUM(ABS(gle.credit_in_account_currency - gle.debit_in_account_currency)) AS amount
|
.select(
|
||||||
FROM
|
gle.voucher_type.as_("doctype"),
|
||||||
`tabGL Entry` gle
|
gle.voucher_no.as_("docname"),
|
||||||
LEFT JOIN
|
gle.account.as_("gl_account"),
|
||||||
`tabAccount` ac ON ac.name = gle.account
|
Sum(Abs(gle.credit_in_account_currency - gle.debit_in_account_currency)).as_("amount"),
|
||||||
WHERE
|
)
|
||||||
ac.account_type = 'Bank'
|
.where(
|
||||||
AND (gle.voucher_type, gle.voucher_no) IN %(docs)s
|
(ac.account_type == "Bank")
|
||||||
AND gle.is_cancelled = 0
|
& Tuple(gle.voucher_type, gle.voucher_no).isin([Tuple(vt, vn) for vt, vn in docs])
|
||||||
GROUP BY
|
& (gle.is_cancelled == 0)
|
||||||
gle.voucher_type, gle.voucher_no, gle.account
|
)
|
||||||
""",
|
.groupby(gle.voucher_type, gle.voucher_no, gle.account)
|
||||||
{"docs": docs},
|
.run(as_dict=True)
|
||||||
as_dict=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
entries = {}
|
entries = {}
|
||||||
@@ -523,31 +523,32 @@ def get_total_allocated_amount(docs):
|
|||||||
if not docs:
|
if not docs:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
# nosemgrep: frappe-semgrep-rules.rules.frappe-using-db-sql
|
# The original window query (ROW_NUMBER/FIRST_VALUE + rownum = 1) just collapses to one
|
||||||
result = frappe.db.sql(
|
# row per (account, payment_document, payment_entry) with the partition's allocation total
|
||||||
"""
|
# and most recent transaction date — i.e. a plain GROUP BY with SUM and MAX.
|
||||||
SELECT total, latest_date, gl_account, payment_document, payment_entry FROM (
|
btp = frappe.qb.DocType("Bank Transaction Payments")
|
||||||
SELECT
|
bt = frappe.qb.DocType("Bank Transaction")
|
||||||
ROW_NUMBER() OVER w AS rownum,
|
ba = frappe.qb.DocType("Bank Account")
|
||||||
SUM(btp.allocated_amount) OVER(PARTITION BY ba.account, btp.payment_document, btp.payment_entry) AS total,
|
|
||||||
FIRST_VALUE(bt.date) OVER w AS latest_date,
|
result = (
|
||||||
ba.account AS gl_account,
|
frappe.qb.from_(btp)
|
||||||
btp.payment_document,
|
.left_join(bt)
|
||||||
btp.payment_entry
|
.on(bt.name == btp.parent)
|
||||||
FROM
|
.left_join(ba)
|
||||||
`tabBank Transaction Payments` btp
|
.on(ba.name == bt.bank_account)
|
||||||
LEFT JOIN `tabBank Transaction` bt ON bt.name=btp.parent
|
.select(
|
||||||
LEFT JOIN `tabBank Account` ba ON ba.name=bt.bank_account
|
Sum(btp.allocated_amount).as_("total"),
|
||||||
WHERE
|
Max(bt.date).as_("latest_date"),
|
||||||
(btp.payment_document, btp.payment_entry) IN %(docs)s
|
ba.account.as_("gl_account"),
|
||||||
AND bt.docstatus = 1
|
btp.payment_document,
|
||||||
WINDOW w AS (PARTITION BY ba.account, btp.payment_document, btp.payment_entry ORDER BY bt.date DESC)
|
btp.payment_entry,
|
||||||
) temp
|
)
|
||||||
WHERE
|
.where(
|
||||||
rownum = 1
|
Tuple(btp.payment_document, btp.payment_entry).isin([Tuple(pd, pe) for pd, pe in docs])
|
||||||
""",
|
& (bt.docstatus == 1)
|
||||||
dict(docs=docs),
|
)
|
||||||
as_dict=True,
|
.groupby(ba.account, btp.payment_document, btp.payment_entry)
|
||||||
|
.run(as_dict=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
payment_allocation_details = {}
|
payment_allocation_details = {}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe.query_builder.functions import Sum
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
|
|
||||||
|
|
||||||
@@ -43,13 +44,17 @@ class CashierClosing(Document):
|
|||||||
self.make_calculations()
|
self.make_calculations()
|
||||||
|
|
||||||
def get_outstanding(self):
|
def get_outstanding(self):
|
||||||
values = frappe.db.sql(
|
si = frappe.qb.DocType("Sales Invoice")
|
||||||
"""
|
values = (
|
||||||
select sum(outstanding_amount)
|
frappe.qb.from_(si)
|
||||||
from `tabSales Invoice`
|
.select(Sum(si.outstanding_amount))
|
||||||
where posting_date=%s and posting_time>=%s and posting_time<=%s and owner=%s
|
.where(
|
||||||
""",
|
(si.posting_date == self.date)
|
||||||
(self.date, self.from_time, self.time, self.user),
|
& (si.posting_time >= self.from_time)
|
||||||
|
& (si.posting_time <= self.time)
|
||||||
|
& (si.owner == self.user)
|
||||||
|
)
|
||||||
|
.run()
|
||||||
)
|
)
|
||||||
self.outstanding_amount = flt(values[0][0] if values else 0)
|
self.outstanding_amount = flt(values[0][0] if values else 0)
|
||||||
|
|
||||||
|
|||||||
@@ -319,56 +319,48 @@ class InvoiceDiscounting(AccountsController):
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_invoices(filters: str):
|
def get_invoices(filters: str):
|
||||||
filters = frappe._dict(json.loads(filters))
|
filters = frappe._dict(json.loads(filters))
|
||||||
cond = []
|
si = frappe.qb.DocType("Sales Invoice")
|
||||||
if filters.customer:
|
di = frappe.qb.DocType("Discounted Invoice")
|
||||||
cond.append("customer=%(customer)s")
|
|
||||||
if filters.from_date:
|
|
||||||
cond.append("posting_date >= %(from_date)s")
|
|
||||||
if filters.to_date:
|
|
||||||
cond.append("posting_date <= %(to_date)s")
|
|
||||||
if filters.min_amount:
|
|
||||||
cond.append("base_grand_total >= %(min_amount)s")
|
|
||||||
if filters.max_amount:
|
|
||||||
cond.append("base_grand_total <= %(max_amount)s")
|
|
||||||
|
|
||||||
where_condition = ""
|
discounted = frappe.qb.from_(di).select(di.sales_invoice).where(di.docstatus == 1)
|
||||||
if cond:
|
|
||||||
where_condition += " and " + " and ".join(cond)
|
|
||||||
|
|
||||||
return frappe.db.sql(
|
query = (
|
||||||
"""
|
frappe.qb.from_(si)
|
||||||
select
|
.select(
|
||||||
name as sales_invoice,
|
si.name.as_("sales_invoice"),
|
||||||
customer,
|
si.customer,
|
||||||
posting_date,
|
si.posting_date,
|
||||||
outstanding_amount,
|
si.outstanding_amount,
|
||||||
debit_to
|
si.debit_to,
|
||||||
from `tabSales Invoice` si
|
)
|
||||||
where
|
.where((si.docstatus == 1) & (si.outstanding_amount > 0) & si.name.notin(discounted))
|
||||||
docstatus = 1
|
|
||||||
and outstanding_amount > 0
|
|
||||||
%s
|
|
||||||
and not exists(select di.name from `tabDiscounted Invoice` di
|
|
||||||
where di.docstatus=1 and di.sales_invoice=si.name)
|
|
||||||
"""
|
|
||||||
% where_condition,
|
|
||||||
filters,
|
|
||||||
as_dict=1,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if filters.customer:
|
||||||
|
query = query.where(si.customer == filters.customer)
|
||||||
|
if filters.from_date:
|
||||||
|
query = query.where(si.posting_date >= filters.from_date)
|
||||||
|
if filters.to_date:
|
||||||
|
query = query.where(si.posting_date <= filters.to_date)
|
||||||
|
if filters.min_amount:
|
||||||
|
query = query.where(si.base_grand_total >= filters.min_amount)
|
||||||
|
if filters.max_amount:
|
||||||
|
query = query.where(si.base_grand_total <= filters.max_amount)
|
||||||
|
|
||||||
|
return query.run(as_dict=1)
|
||||||
|
|
||||||
|
|
||||||
def get_party_account_based_on_invoice_discounting(sales_invoice):
|
def get_party_account_based_on_invoice_discounting(sales_invoice):
|
||||||
party_account = None
|
party_account = None
|
||||||
invoice_discounting = frappe.db.sql(
|
par = frappe.qb.DocType("Invoice Discounting")
|
||||||
"""
|
ch = frappe.qb.DocType("Discounted Invoice")
|
||||||
select par.accounts_receivable_discounted, par.accounts_receivable_unpaid, par.status
|
invoice_discounting = (
|
||||||
from `tabInvoice Discounting` par, `tabDiscounted Invoice` ch
|
frappe.qb.from_(par)
|
||||||
where par.name=ch.parent
|
.inner_join(ch)
|
||||||
and par.docstatus=1
|
.on(par.name == ch.parent)
|
||||||
and ch.sales_invoice = %s
|
.select(par.accounts_receivable_discounted, par.accounts_receivable_unpaid, par.status)
|
||||||
""",
|
.where((par.docstatus == 1) & (ch.sales_invoice == sales_invoice))
|
||||||
(sales_invoice),
|
.run(as_dict=1)
|
||||||
as_dict=1,
|
|
||||||
)
|
)
|
||||||
if invoice_discounting:
|
if invoice_discounting:
|
||||||
if invoice_discounting[0].status == "Disbursed":
|
if invoice_discounting[0].status == "Disbursed":
|
||||||
|
|||||||
@@ -52,12 +52,11 @@ class ModeofPayment(Document):
|
|||||||
|
|
||||||
def validate_pos_mode_of_payment(self):
|
def validate_pos_mode_of_payment(self):
|
||||||
if not self.enabled:
|
if not self.enabled:
|
||||||
pos_profiles = frappe.db.sql(
|
pos_profiles = frappe.get_all(
|
||||||
"""SELECT sip.parent FROM `tabSales Invoice Payment` sip
|
"Sales Invoice Payment",
|
||||||
WHERE sip.parenttype = 'POS Profile' and sip.mode_of_payment = %s""",
|
filters={"parenttype": "POS Profile", "mode_of_payment": self.name},
|
||||||
(self.name),
|
pluck="parent",
|
||||||
)
|
)
|
||||||
pos_profiles = list(map(lambda x: x[0], pos_profiles))
|
|
||||||
|
|
||||||
if pos_profiles:
|
if pos_profiles:
|
||||||
message = _(
|
message = _(
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import frappe
|
|||||||
from frappe import ValidationError, _, qb, scrub, throw
|
from frappe import ValidationError, _, qb, scrub, throw
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.model.meta import get_field_precision
|
from frappe.model.meta import get_field_precision
|
||||||
from frappe.query_builder import Tuple
|
from frappe.query_builder import Case, Tuple
|
||||||
from frappe.query_builder.functions import Count
|
from frappe.query_builder.functions import Abs, Count, Max
|
||||||
from frappe.utils import cint, comma_or, flt, getdate, nowdate
|
from frappe.utils import cint, comma_or, flt, getdate, nowdate
|
||||||
from frappe.utils.data import comma_and, fmt_money, get_link_to_form
|
from frappe.utils.data import comma_and, fmt_money, get_link_to_form
|
||||||
from pypika.functions import Coalesce, Sum
|
from pypika.functions import Coalesce, Sum
|
||||||
@@ -766,13 +766,19 @@ class PaymentEntry(AccountsController):
|
|||||||
def validate_journal_entry(self):
|
def validate_journal_entry(self):
|
||||||
for d in self.get("references"):
|
for d in self.get("references"):
|
||||||
if d.allocated_amount and d.reference_doctype == "Journal Entry":
|
if d.allocated_amount and d.reference_doctype == "Journal Entry":
|
||||||
je_accounts = frappe.db.sql(
|
je_accounts = frappe.get_all(
|
||||||
"""select debit, credit from `tabJournal Entry Account`
|
"Journal Entry Account",
|
||||||
where account = %s and party=%s and docstatus = 1 and parent = %s
|
filters={
|
||||||
and (reference_type is null or reference_type in ("", "Sales Order", "Purchase Order"))
|
"account": self.party_account,
|
||||||
""",
|
"party": self.party,
|
||||||
(self.party_account, self.party, d.reference_name),
|
"docstatus": 1,
|
||||||
as_dict=True,
|
"parent": d.reference_name,
|
||||||
|
},
|
||||||
|
or_filters=[
|
||||||
|
["reference_type", "is", "not set"],
|
||||||
|
["reference_type", "in", ["Sales Order", "Purchase Order"]],
|
||||||
|
],
|
||||||
|
fields=["debit", "credit"],
|
||||||
)
|
)
|
||||||
|
|
||||||
if not je_accounts:
|
if not je_accounts:
|
||||||
@@ -857,27 +863,17 @@ class PaymentEntry(AccountsController):
|
|||||||
)
|
)
|
||||||
base_outstanding = flt(allocated_amount * conversion_rate, base_outstanding_precision)
|
base_outstanding = flt(allocated_amount * conversion_rate, base_outstanding_precision)
|
||||||
|
|
||||||
|
ps = frappe.qb.DocType("Payment Schedule")
|
||||||
if cancel:
|
if cancel:
|
||||||
frappe.db.sql(
|
(
|
||||||
"""
|
frappe.qb.update(ps)
|
||||||
UPDATE `tabPayment Schedule`
|
.set(ps.paid_amount, ps.paid_amount - (allocated_amount - discounted_amt))
|
||||||
SET
|
.set(ps.base_paid_amount, ps.base_paid_amount - base_paid_amount)
|
||||||
paid_amount = `paid_amount` - %s,
|
.set(ps.discounted_amount, ps.discounted_amount - discounted_amt)
|
||||||
base_paid_amount = `base_paid_amount` - %s,
|
.set(ps.outstanding, ps.outstanding + allocated_amount)
|
||||||
discounted_amount = `discounted_amount` - %s,
|
.set(ps.base_outstanding, ps.base_outstanding - base_outstanding)
|
||||||
outstanding = `outstanding` + %s,
|
.where((ps.parent == key[1]) & (ps.payment_term == key[0]))
|
||||||
base_outstanding = `base_outstanding` - %s
|
).run()
|
||||||
WHERE parent = %s and payment_term = %s""",
|
|
||||||
(
|
|
||||||
allocated_amount - discounted_amt,
|
|
||||||
base_paid_amount,
|
|
||||||
discounted_amt,
|
|
||||||
allocated_amount,
|
|
||||||
base_outstanding,
|
|
||||||
key[1],
|
|
||||||
key[0],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
if allocated_amount > outstanding:
|
if allocated_amount > outstanding:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
@@ -887,26 +883,15 @@ class PaymentEntry(AccountsController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if allocated_amount and outstanding:
|
if allocated_amount and outstanding:
|
||||||
frappe.db.sql(
|
(
|
||||||
"""
|
frappe.qb.update(ps)
|
||||||
UPDATE `tabPayment Schedule`
|
.set(ps.paid_amount, ps.paid_amount + (allocated_amount - discounted_amt))
|
||||||
SET
|
.set(ps.base_paid_amount, ps.base_paid_amount + base_paid_amount)
|
||||||
paid_amount = `paid_amount` + %s,
|
.set(ps.discounted_amount, ps.discounted_amount + discounted_amt)
|
||||||
base_paid_amount = `base_paid_amount` + %s,
|
.set(ps.outstanding, ps.outstanding - allocated_amount)
|
||||||
discounted_amount = `discounted_amount` + %s,
|
.set(ps.base_outstanding, ps.base_outstanding - base_outstanding)
|
||||||
outstanding = `outstanding` - %s,
|
.where((ps.parent == key[1]) & (ps.payment_term == key[0]))
|
||||||
base_outstanding = `base_outstanding` - %s
|
).run()
|
||||||
WHERE parent = %s and payment_term = %s""",
|
|
||||||
(
|
|
||||||
allocated_amount - discounted_amt,
|
|
||||||
base_paid_amount,
|
|
||||||
discounted_amt,
|
|
||||||
allocated_amount,
|
|
||||||
base_outstanding,
|
|
||||||
key[1],
|
|
||||||
key[0],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_allocated_amount_in_transaction_currency(
|
def get_allocated_amount_in_transaction_currency(
|
||||||
self, allocated_amount, reference_doctype, reference_docname
|
self, allocated_amount, reference_doctype, reference_docname
|
||||||
@@ -1216,11 +1201,7 @@ class PaymentEntry(AccountsController):
|
|||||||
# Clear the reference document which doesn't have allocated amount on validate so that form can be loaded fast
|
# Clear the reference document which doesn't have allocated amount on validate so that form can be loaded fast
|
||||||
def clear_unallocated_reference_document_rows(self):
|
def clear_unallocated_reference_document_rows(self):
|
||||||
self.set("references", self.get("references", {"allocated_amount": ["not in", [0, None, ""]]}))
|
self.set("references", self.get("references", {"allocated_amount": ["not in", [0, None, ""]]}))
|
||||||
frappe.db.sql(
|
frappe.db.delete("Payment Entry Reference", {"parent": self.name, "allocated_amount": 0})
|
||||||
"""delete from `tabPayment Entry Reference`
|
|
||||||
where parent = %s and allocated_amount = 0""",
|
|
||||||
self.name,
|
|
||||||
)
|
|
||||||
|
|
||||||
def set_title(self):
|
def set_title(self):
|
||||||
if frappe.flags.in_import and self.title:
|
if frappe.flags.in_import and self.title:
|
||||||
@@ -1876,7 +1857,7 @@ def get_matched_payment_request_of_references(references=None):
|
|||||||
PR.reference_doctype,
|
PR.reference_doctype,
|
||||||
PR.reference_name,
|
PR.reference_name,
|
||||||
PR.outstanding_amount.as_("allocated_amount"),
|
PR.outstanding_amount.as_("allocated_amount"),
|
||||||
PR.name.as_("payment_request"),
|
Max(PR.name).as_("payment_request"), # count == 1 below ⇒ one row per group; postgres-safe
|
||||||
Count("*").as_("count"),
|
Count("*").as_("count"),
|
||||||
)
|
)
|
||||||
.where(Tuple(PR.reference_doctype, PR.reference_name, PR.outstanding_amount).isin(refs))
|
.where(Tuple(PR.reference_doctype, PR.reference_name, PR.outstanding_amount).isin(refs))
|
||||||
@@ -2315,12 +2296,7 @@ def get_orders_to_be_billed(
|
|||||||
if not voucher_type:
|
if not voucher_type:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# dynamic dimension filters
|
|
||||||
condition = ""
|
|
||||||
active_dimensions = get_dimensions(True)[0]
|
active_dimensions = get_dimensions(True)[0]
|
||||||
for dim in active_dimensions:
|
|
||||||
if filters.get(dim.fieldname):
|
|
||||||
condition += f" and {dim.fieldname}={frappe.db.escape(filters.get(dim.fieldname))}"
|
|
||||||
|
|
||||||
if party_account_currency == company_currency:
|
if party_account_currency == company_currency:
|
||||||
grand_total_field = "base_grand_total"
|
grand_total_field = "base_grand_total"
|
||||||
@@ -2329,38 +2305,38 @@ def get_orders_to_be_billed(
|
|||||||
grand_total_field = "grand_total"
|
grand_total_field = "grand_total"
|
||||||
rounded_total_field = "rounded_total"
|
rounded_total_field = "rounded_total"
|
||||||
|
|
||||||
orders = frappe.db.sql(
|
voucher = frappe.qb.DocType(voucher_type)
|
||||||
"""
|
invoice_amount = (
|
||||||
select
|
Case()
|
||||||
name as voucher_no,
|
.when(voucher[rounded_total_field] != 0, voucher[rounded_total_field])
|
||||||
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
|
.else_(voucher[grand_total_field])
|
||||||
(if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount,
|
|
||||||
transaction_date as posting_date
|
|
||||||
from
|
|
||||||
`tab{voucher_type}`
|
|
||||||
where
|
|
||||||
{party_type} = %s
|
|
||||||
and docstatus = 1
|
|
||||||
and company = %s
|
|
||||||
and status != "Closed"
|
|
||||||
and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid
|
|
||||||
and abs(100 - per_billed) > 0.01
|
|
||||||
{condition}
|
|
||||||
order by
|
|
||||||
transaction_date, name
|
|
||||||
""".format(
|
|
||||||
**{
|
|
||||||
"rounded_total_field": rounded_total_field,
|
|
||||||
"grand_total_field": grand_total_field,
|
|
||||||
"voucher_type": voucher_type,
|
|
||||||
"party_type": scrub(party_type),
|
|
||||||
"condition": condition,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
(party, company),
|
|
||||||
as_dict=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
query = (
|
||||||
|
frappe.qb.from_(voucher)
|
||||||
|
.select(
|
||||||
|
voucher.name.as_("voucher_no"),
|
||||||
|
invoice_amount.as_("invoice_amount"),
|
||||||
|
(invoice_amount - voucher.advance_paid).as_("outstanding_amount"),
|
||||||
|
voucher.transaction_date.as_("posting_date"),
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
(voucher[scrub(party_type)] == party)
|
||||||
|
& (voucher.docstatus == 1)
|
||||||
|
& (voucher.company == company)
|
||||||
|
& (voucher.status != "Closed")
|
||||||
|
& (invoice_amount > voucher.advance_paid)
|
||||||
|
& (Abs(100 - voucher.per_billed) > 0.01)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# dynamic dimension filters
|
||||||
|
for dim in active_dimensions:
|
||||||
|
if filters.get(dim.fieldname):
|
||||||
|
query = query.where(voucher[dim.fieldname] == filters.get(dim.fieldname))
|
||||||
|
|
||||||
|
orders = query.orderby(voucher.transaction_date).orderby(voucher.name).run(as_dict=True)
|
||||||
|
|
||||||
order_list = []
|
order_list = []
|
||||||
for d in orders:
|
for d in orders:
|
||||||
if (
|
if (
|
||||||
@@ -2409,8 +2385,8 @@ def get_negative_outstanding_invoices(
|
|||||||
return frappe.db.sql(
|
return frappe.db.sql(
|
||||||
"""
|
"""
|
||||||
select
|
select
|
||||||
"{voucher_type}" as voucher_type, name as voucher_no, {account} as account,
|
'{voucher_type}' as voucher_type, name as voucher_no, {account} as account,
|
||||||
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
|
coalesce(nullif({rounded_total_field}, 0), {grand_total_field}) as invoice_amount,
|
||||||
outstanding_amount, posting_date,
|
outstanding_amount, posting_date,
|
||||||
due_date, conversion_rate as exchange_rate
|
due_date, conversion_rate as exchange_rate
|
||||||
from
|
from
|
||||||
@@ -3272,27 +3248,28 @@ def get_reference_as_per_payment_terms(
|
|||||||
|
|
||||||
|
|
||||||
def get_paid_amount(dt, dn, party_type, party, account, due_date):
|
def get_paid_amount(dt, dn, party_type, party, account, due_date):
|
||||||
|
gle = frappe.qb.DocType("GL Entry")
|
||||||
if party_type == "Customer":
|
if party_type == "Customer":
|
||||||
dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
|
dr_or_cr = gle.credit_in_account_currency - gle.debit_in_account_currency
|
||||||
else:
|
else:
|
||||||
dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
|
dr_or_cr = gle.debit_in_account_currency - gle.credit_in_account_currency
|
||||||
|
|
||||||
paid_amount = frappe.db.sql(
|
paid_amount = (
|
||||||
f"""
|
frappe.qb.from_(gle)
|
||||||
select ifnull(sum({dr_or_cr}), 0) as paid_amount
|
.select(Sum(dr_or_cr))
|
||||||
from `tabGL Entry`
|
.where(
|
||||||
where against_voucher_type = %s
|
(gle.against_voucher_type == dt)
|
||||||
and against_voucher = %s
|
& (gle.against_voucher == dn)
|
||||||
and party_type = %s
|
& (gle.party_type == party_type)
|
||||||
and party = %s
|
& (gle.party == party)
|
||||||
and account = %s
|
& (gle.account == account)
|
||||||
and due_date = %s
|
& (gle.due_date == due_date)
|
||||||
and {dr_or_cr} > 0
|
& (dr_or_cr > 0)
|
||||||
""",
|
)
|
||||||
(dt, dn, party_type, party, account, due_date),
|
.run()
|
||||||
)
|
)
|
||||||
|
|
||||||
return paid_amount[0][0] if paid_amount else 0
|
return (paid_amount[0][0] or 0) if paid_amount else 0
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -60,23 +60,32 @@ class PaymentOrder(Document):
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def get_mop_query(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
|
def get_mop_query(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
|
||||||
return frappe.db.sql(
|
return frappe.get_all(
|
||||||
""" select mode_of_payment from `tabPayment Order Reference`
|
"Payment Order Reference",
|
||||||
where parent = %(parent)s and mode_of_payment like %(txt)s
|
filters={"parent": filters.get("parent"), "mode_of_payment": ["like", f"%{txt}%"]},
|
||||||
limit %(page_len)s offset %(start)s""",
|
fields=["mode_of_payment"],
|
||||||
{"parent": filters.get("parent"), "start": start, "page_len": page_len, "txt": "%%%s%%" % txt},
|
limit_start=start,
|
||||||
|
limit_page_length=page_len,
|
||||||
|
order_by="", # match the original query (no ORDER BY); avoid get_all's default sort
|
||||||
|
as_list=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def get_supplier_query(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
|
def get_supplier_query(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
|
||||||
return frappe.db.sql(
|
return frappe.get_all(
|
||||||
""" select supplier from `tabPayment Order Reference`
|
"Payment Order Reference",
|
||||||
where parent = %(parent)s and supplier like %(txt)s and
|
filters={
|
||||||
(payment_reference is null or payment_reference='')
|
"parent": filters.get("parent"),
|
||||||
limit %(page_len)s offset %(start)s""",
|
"supplier": ["like", f"%{txt}%"],
|
||||||
{"parent": filters.get("parent"), "start": start, "page_len": page_len, "txt": "%%%s%%" % txt},
|
"payment_reference": ["is", "not set"],
|
||||||
|
},
|
||||||
|
fields=["supplier"],
|
||||||
|
limit_start=start,
|
||||||
|
limit_page_length=page_len,
|
||||||
|
order_by="", # match the original query (no ORDER BY); avoid get_all's default sort
|
||||||
|
as_list=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -629,11 +629,9 @@ class PaymentRequest(Document):
|
|||||||
|
|
||||||
def check_if_payment_entry_exists(self):
|
def check_if_payment_entry_exists(self):
|
||||||
if self.status == "Paid":
|
if self.status == "Paid":
|
||||||
if frappe.get_all(
|
if frappe.db.exists(
|
||||||
"Payment Entry Reference",
|
"Payment Entry Reference",
|
||||||
filters={"reference_name": self.reference_name, "docstatus": ["<", 2]},
|
{"reference_name": self.reference_name, "docstatus": ["<", 2]},
|
||||||
fields=["parent"],
|
|
||||||
limit=1,
|
|
||||||
):
|
):
|
||||||
frappe.throw(_("Payment Entry already exists"), title=_("Error"))
|
frappe.throw(_("Payment Entry already exists"), title=_("Error"))
|
||||||
|
|
||||||
@@ -1212,10 +1210,11 @@ def get_dummy_message(doc):
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_subscription_details(reference_doctype: str, reference_name: str):
|
def get_subscription_details(reference_doctype: str, reference_name: str):
|
||||||
if reference_doctype == "Sales Invoice":
|
if reference_doctype == "Sales Invoice":
|
||||||
subscriptions = frappe.db.sql(
|
subscriptions = frappe.get_all(
|
||||||
"""SELECT parent as sub_name FROM `tabSubscription Invoice` WHERE invoice=%s""",
|
"Subscription Invoice",
|
||||||
reference_name,
|
filters={"invoice": reference_name},
|
||||||
as_dict=1,
|
fields=["parent as sub_name"],
|
||||||
|
order_by="", # match the original query (no ORDER BY); avoid get_all's default sort
|
||||||
)
|
)
|
||||||
subscription_plans = []
|
subscription_plans = []
|
||||||
for subscription in subscriptions:
|
for subscription in subscriptions:
|
||||||
|
|||||||
Reference in New Issue
Block a user