Merge branch 'develop' into subcontract-supplied-items

This commit is contained in:
Marica 2022-06-13 18:32:43 +05:30 committed by GitHub
commit f5ebaed191
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 208 additions and 271 deletions

View File

@ -115,4 +115,5 @@ jobs:
echo "Updating to latest version" echo "Updating to latest version"
git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}" git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}"
git -C "apps/erpnext" checkout -q -f "$GITHUB_SHA" git -C "apps/erpnext" checkout -q -f "$GITHUB_SHA"
bench setup requirements --python
bench --site test_site migrate bench --site test_site migrate

View File

@ -9,6 +9,8 @@ pull_request_rules:
- author!=nabinhait - author!=nabinhait
- author!=ankush - author!=ankush
- author!=deepeshgarg007 - author!=deepeshgarg007
- author!=mergify[bot]
- or: - or:
- base=version-13 - base=version-13
- base=version-12 - base=version-12
@ -19,6 +21,16 @@ pull_request_rules:
@{{author}}, thanks for the contribution, but we do not accept pull requests on a stable branch. Please raise PR on an appropriate hotfix branch. @{{author}}, thanks for the contribution, but we do not accept pull requests on a stable branch. Please raise PR on an appropriate hotfix branch.
https://github.com/frappe/erpnext/wiki/Pull-Request-Checklist#which-branch https://github.com/frappe/erpnext/wiki/Pull-Request-Checklist#which-branch
- name: Auto-close PRs on pre-release branch
conditions:
- base=version-13-pre-release
actions:
close:
comment:
message: |
@{{author}}, pre-release branch is not maintained anymore. Releases are directly done by merging hotfix branch to stable branches.
- name: backport to develop - name: backport to develop
conditions: conditions:
- label="backport develop" - label="backport develop"

View File

@ -172,11 +172,6 @@ frappe.query_reports["Accounts Receivable"] = {
"label": __("Show Sales Person"), "label": __("Show Sales Person"),
"fieldtype": "Check", "fieldtype": "Check",
}, },
{
"fieldname": "show_remarks",
"label": __("Show Remarks"),
"fieldtype": "Check",
},
{ {
"fieldname": "tax_id", "fieldname": "tax_id",
"label": __("Tax Id"), "label": __("Tax Id"),

View File

@ -5,7 +5,9 @@
from collections import OrderedDict from collections import OrderedDict
import frappe import frappe
from frappe import _, scrub from frappe import _, qb, scrub
from frappe.query_builder import Criterion
from frappe.query_builder.functions import Date
from frappe.utils import cint, cstr, flt, getdate, nowdate from frappe.utils import cint, cstr, flt, getdate, nowdate
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
@ -41,6 +43,8 @@ def execute(filters=None):
class ReceivablePayableReport(object): class ReceivablePayableReport(object):
def __init__(self, filters=None): def __init__(self, filters=None):
self.filters = frappe._dict(filters or {}) self.filters = frappe._dict(filters or {})
self.qb_selection_filter = []
self.ple = qb.DocType("Payment Ledger Entry")
self.filters.report_date = getdate(self.filters.report_date or nowdate()) self.filters.report_date = getdate(self.filters.report_date or nowdate())
self.age_as_on = ( self.age_as_on = (
getdate(nowdate()) getdate(nowdate())
@ -78,7 +82,7 @@ class ReceivablePayableReport(object):
self.skip_total_row = 1 self.skip_total_row = 1
def get_data(self): def get_data(self):
self.get_gl_entries() self.get_ple_entries()
self.get_sales_invoices_or_customers_based_on_sales_person() self.get_sales_invoices_or_customers_based_on_sales_person()
self.voucher_balance = OrderedDict() self.voucher_balance = OrderedDict()
self.init_voucher_balance() # invoiced, paid, credit_note, outstanding self.init_voucher_balance() # invoiced, paid, credit_note, outstanding
@ -96,25 +100,25 @@ class ReceivablePayableReport(object):
self.get_return_entries() self.get_return_entries()
self.data = [] self.data = []
for gle in self.gl_entries:
self.update_voucher_balance(gle) for ple in self.ple_entries:
self.update_voucher_balance(ple)
self.build_data() self.build_data()
def init_voucher_balance(self): def init_voucher_balance(self):
# build all keys, since we want to exclude vouchers beyond the report date # build all keys, since we want to exclude vouchers beyond the report date
for gle in self.gl_entries: for ple in self.ple_entries:
# get the balance object for voucher_type # get the balance object for voucher_type
key = (gle.voucher_type, gle.voucher_no, gle.party) key = (ple.voucher_type, ple.voucher_no, ple.party)
if not key in self.voucher_balance: if not key in self.voucher_balance:
self.voucher_balance[key] = frappe._dict( self.voucher_balance[key] = frappe._dict(
voucher_type=gle.voucher_type, voucher_type=ple.voucher_type,
voucher_no=gle.voucher_no, voucher_no=ple.voucher_no,
party=gle.party, party=ple.party,
party_account=gle.account, party_account=ple.account,
posting_date=gle.posting_date, posting_date=ple.posting_date,
account_currency=gle.account_currency, account_currency=ple.account_currency,
remarks=gle.remarks if self.filters.get("show_remarks") else None,
invoiced=0.0, invoiced=0.0,
paid=0.0, paid=0.0,
credit_note=0.0, credit_note=0.0,
@ -124,23 +128,22 @@ class ReceivablePayableReport(object):
credit_note_in_account_currency=0.0, credit_note_in_account_currency=0.0,
outstanding_in_account_currency=0.0, outstanding_in_account_currency=0.0,
) )
self.get_invoices(gle)
if self.filters.get("group_by_party"): if self.filters.get("group_by_party"):
self.init_subtotal_row(gle.party) self.init_subtotal_row(ple.party)
if self.filters.get("group_by_party"): if self.filters.get("group_by_party"):
self.init_subtotal_row("Total") self.init_subtotal_row("Total")
def get_invoices(self, gle): def get_invoices(self, ple):
if gle.voucher_type in ("Sales Invoice", "Purchase Invoice"): if ple.voucher_type in ("Sales Invoice", "Purchase Invoice"):
if self.filters.get("sales_person"): if self.filters.get("sales_person"):
if gle.voucher_no in self.sales_person_records.get( if ple.voucher_no in self.sales_person_records.get(
"Sales Invoice", [] "Sales Invoice", []
) or gle.party in self.sales_person_records.get("Customer", []): ) or ple.party in self.sales_person_records.get("Customer", []):
self.invoices.add(gle.voucher_no) self.invoices.add(ple.voucher_no)
else: else:
self.invoices.add(gle.voucher_no) self.invoices.add(ple.voucher_no)
def init_subtotal_row(self, party): def init_subtotal_row(self, party):
if not self.total_row_map.get(party): if not self.total_row_map.get(party):
@ -162,39 +165,49 @@ class ReceivablePayableReport(object):
"range5", "range5",
] ]
def update_voucher_balance(self, gle): def get_voucher_balance(self, ple):
if self.filters.get("sales_person"):
if not (
ple.party in self.sales_person_records.get("Customer", [])
or ple.against_voucher_no in self.sales_person_records.get("Sales Invoice", [])
):
return
key = (ple.against_voucher_type, ple.against_voucher_no, ple.party)
row = self.voucher_balance.get(key)
return row
def update_voucher_balance(self, ple):
# get the row where this balance needs to be updated # get the row where this balance needs to be updated
# if its a payment, it will return the linked invoice or will be considered as advance # if its a payment, it will return the linked invoice or will be considered as advance
row = self.get_voucher_balance(gle) row = self.get_voucher_balance(ple)
if not row: if not row:
return return
# gle_balance will be the total "debit - credit" for receivable type reports and
# and vice-versa for payable type reports
gle_balance = self.get_gle_balance(gle)
gle_balance_in_account_currency = self.get_gle_balance_in_account_currency(gle)
if gle_balance > 0: amount = ple.amount
if gle.voucher_type in ("Journal Entry", "Payment Entry") and gle.against_voucher: amount_in_account_currency = ple.amount_in_account_currency
# debit against sales / purchase invoice
row.paid -= gle_balance # update voucher
row.paid_in_account_currency -= gle_balance_in_account_currency if ple.amount > 0:
if (
ple.voucher_type in ["Journal Entry", "Payment Entry"]
and ple.voucher_no != ple.against_voucher_no
):
row.paid -= amount
row.paid_in_account_currency -= amount_in_account_currency
else: else:
# invoice row.invoiced += amount
row.invoiced += gle_balance row.invoiced_in_account_currency += amount_in_account_currency
row.invoiced_in_account_currency += gle_balance_in_account_currency
else: else:
# payment or credit note for receivables if self.is_invoice(ple):
if self.is_invoice(gle): row.credit_note -= amount
# stand alone debit / credit note row.credit_note_in_account_currency -= amount_in_account_currency
row.credit_note -= gle_balance
row.credit_note_in_account_currency -= gle_balance_in_account_currency
else: else:
# advance / unlinked payment or other adjustment row.paid -= amount
row.paid -= gle_balance row.paid_in_account_currency -= amount_in_account_currency
row.paid_in_account_currency -= gle_balance_in_account_currency
if gle.cost_center: if ple.cost_center:
row.cost_center = str(gle.cost_center) row.cost_center = str(ple.cost_center)
def update_sub_total_row(self, row, party): def update_sub_total_row(self, row, party):
total_row = self.total_row_map.get(party) total_row = self.total_row_map.get(party)
@ -210,39 +223,6 @@ class ReceivablePayableReport(object):
self.data.append({}) self.data.append({})
self.update_sub_total_row(sub_total_row, "Total") self.update_sub_total_row(sub_total_row, "Total")
def get_voucher_balance(self, gle):
if self.filters.get("sales_person"):
against_voucher = gle.against_voucher or gle.voucher_no
if not (
gle.party in self.sales_person_records.get("Customer", [])
or against_voucher in self.sales_person_records.get("Sales Invoice", [])
):
return
voucher_balance = None
if gle.against_voucher:
# find invoice
against_voucher = gle.against_voucher
# If payment is made against credit note
# and credit note is made against a Sales Invoice
# then consider the payment against original sales invoice.
if gle.against_voucher_type in ("Sales Invoice", "Purchase Invoice"):
if gle.against_voucher in self.return_entries:
return_against = self.return_entries.get(gle.against_voucher)
if return_against:
against_voucher = return_against
voucher_balance = self.voucher_balance.get(
(gle.against_voucher_type, against_voucher, gle.party)
)
if not voucher_balance:
# no invoice, this is an invoice / stand-alone payment / credit note
voucher_balance = self.voucher_balance.get((gle.voucher_type, gle.voucher_no, gle.party))
return voucher_balance
def build_data(self): def build_data(self):
# set outstanding for all the accumulated balances # set outstanding for all the accumulated balances
# as we can use this to filter out invoices without outstanding # as we can use this to filter out invoices without outstanding
@ -260,6 +240,7 @@ class ReceivablePayableReport(object):
if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) and ( if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) and (
abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision
): ):
# non-zero oustanding, we must consider this row # non-zero oustanding, we must consider this row
if self.is_invoice(row) and self.filters.based_on_payment_terms: if self.is_invoice(row) and self.filters.based_on_payment_terms:
@ -669,48 +650,53 @@ class ReceivablePayableReport(object):
index = 4 index = 4
row["range" + str(index + 1)] = row.outstanding row["range" + str(index + 1)] = row.outstanding
def get_gl_entries(self): def get_ple_entries(self):
# get all the GL entries filtered by the given filters # get all the GL entries filtered by the given filters
conditions, values = self.prepare_conditions() self.prepare_conditions()
order_by = self.get_order_by_condition()
if self.filters.show_future_payments: if self.filters.show_future_payments:
values.insert(2, self.filters.report_date) self.qb_selection_filter.append(
(
date_condition = """AND (posting_date <= %s self.ple.posting_date.lte(self.filters.report_date)
OR (against_voucher IS NULL AND DATE(creation) <= %s))""" | (
(self.ple.voucher_no == self.ple.against_voucher_no)
& (Date(self.ple.creation).lte(self.filters.report_date))
)
)
)
else: else:
date_condition = "AND posting_date <=%s" self.qb_selection_filter.append(self.ple.posting_date.lte(self.filters.report_date))
if self.filters.get(scrub(self.party_type)): ple = qb.DocType("Payment Ledger Entry")
select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit" query = (
else: qb.from_(ple)
select_fields = "debit, credit" .select(
ple.account,
doc_currency_fields = "debit_in_account_currency, credit_in_account_currency" ple.voucher_type,
ple.voucher_no,
remarks = ", remarks" if self.filters.get("show_remarks") else "" ple.against_voucher_type,
ple.against_voucher_no,
self.gl_entries = frappe.db.sql( ple.party_type,
""" ple.cost_center,
select ple.party,
name, posting_date, account, party_type, party, voucher_type, voucher_no, cost_center, ple.posting_date,
against_voucher_type, against_voucher, account_currency, {0}, {1} {remarks} ple.due_date,
from ple.account_currency.as_("currency"),
`tabGL Entry` ple.amount,
where ple.amount_in_account_currency,
docstatus < 2 )
and is_cancelled = 0 .where(ple.delinked == 0)
and party_type=%s .where(Criterion.all(self.qb_selection_filter))
and (party is not null and party != '')
{2} {3} {4}""".format(
select_fields, doc_currency_fields, date_condition, conditions, order_by, remarks=remarks
),
values,
as_dict=True,
) )
if self.filters.get("group_by_party"):
query = query.orderby(self.ple.party, self.ple.posting_date)
else:
query = query.orderby(self.ple.posting_date, self.ple.party)
self.ple_entries = query.run(as_dict=True)
def get_sales_invoices_or_customers_based_on_sales_person(self): def get_sales_invoices_or_customers_based_on_sales_person(self):
if self.filters.get("sales_person"): if self.filters.get("sales_person"):
lft, rgt = frappe.db.get_value("Sales Person", self.filters.get("sales_person"), ["lft", "rgt"]) lft, rgt = frappe.db.get_value("Sales Person", self.filters.get("sales_person"), ["lft", "rgt"])
@ -731,23 +717,21 @@ class ReceivablePayableReport(object):
self.sales_person_records.setdefault(d.parenttype, set()).add(d.parent) self.sales_person_records.setdefault(d.parenttype, set()).add(d.parent)
def prepare_conditions(self): def prepare_conditions(self):
conditions = [""] self.qb_selection_filter = []
values = [self.party_type, self.filters.report_date]
party_type_field = scrub(self.party_type) party_type_field = scrub(self.party_type)
self.add_common_filters(conditions, values, party_type_field) self.add_common_filters(party_type_field=party_type_field)
if party_type_field == "customer": if party_type_field == "customer":
self.add_customer_filters(conditions, values) self.add_customer_filters()
elif party_type_field == "supplier": elif party_type_field == "supplier":
self.add_supplier_filters(conditions, values) self.add_supplier_filters()
if self.filters.cost_center: if self.filters.cost_center:
self.get_cost_center_conditions(conditions) self.get_cost_center_conditions()
self.add_accounting_dimensions_filters(conditions, values) self.add_accounting_dimensions_filters()
return " and ".join(conditions), values
def get_cost_center_conditions(self, conditions): def get_cost_center_conditions(self, conditions):
lft, rgt = frappe.db.get_value("Cost Center", self.filters.cost_center, ["lft", "rgt"]) lft, rgt = frappe.db.get_value("Cost Center", self.filters.cost_center, ["lft", "rgt"])
@ -755,32 +739,20 @@ class ReceivablePayableReport(object):
center.name center.name
for center in frappe.get_list("Cost Center", filters={"lft": (">=", lft), "rgt": ("<=", rgt)}) for center in frappe.get_list("Cost Center", filters={"lft": (">=", lft), "rgt": ("<=", rgt)})
] ]
self.qb_selection_filter.append(self.ple.cost_center.isin(cost_center_list))
cost_center_string = '", "'.join(cost_center_list) def add_common_filters(self, party_type_field):
conditions.append('cost_center in ("{0}")'.format(cost_center_string))
def get_order_by_condition(self):
if self.filters.get("group_by_party"):
return "order by party, posting_date"
else:
return "order by posting_date, party"
def add_common_filters(self, conditions, values, party_type_field):
if self.filters.company: if self.filters.company:
conditions.append("company=%s") self.qb_selection_filter.append(self.ple.company == self.filters.company)
values.append(self.filters.company)
if self.filters.finance_book: if self.filters.finance_book:
conditions.append("ifnull(finance_book, '') in (%s, '')") self.qb_selection_filter.append(self.ple.finance_book == self.filters.finance_book)
values.append(self.filters.finance_book)
if self.filters.get(party_type_field): if self.filters.get(party_type_field):
conditions.append("party=%s") self.qb_selection_filter.append(self.ple.party == self.filters.get(party_type_field))
values.append(self.filters.get(party_type_field))
if self.filters.party_account: if self.filters.party_account:
conditions.append("account =%s") self.qb_selection_filter.append(self.ple.account == self.filters.party_account)
values.append(self.filters.party_account)
else: else:
# get GL with "receivable" or "payable" account_type # get GL with "receivable" or "payable" account_type
account_type = "Receivable" if self.party_type == "Customer" else "Payable" account_type = "Receivable" if self.party_type == "Customer" else "Payable"
@ -792,46 +764,68 @@ class ReceivablePayableReport(object):
] ]
if accounts: if accounts:
conditions.append("account in (%s)" % ",".join(["%s"] * len(accounts))) self.qb_selection_filter.append(self.ple.account.isin(accounts))
values += accounts
def add_customer_filters(
self,
):
self.customter = qb.DocType("Customer")
def add_customer_filters(self, conditions, values):
if self.filters.get("customer_group"): if self.filters.get("customer_group"):
conditions.append(self.get_hierarchical_filters("Customer Group", "customer_group")) self.get_hierarchical_filters("Customer Group", "customer_group")
if self.filters.get("territory"): if self.filters.get("territory"):
conditions.append(self.get_hierarchical_filters("Territory", "territory")) self.get_hierarchical_filters("Territory", "territory")
if self.filters.get("payment_terms_template"): if self.filters.get("payment_terms_template"):
conditions.append("party in (select name from tabCustomer where payment_terms=%s)") self.qb_selection_filter.append(
values.append(self.filters.get("payment_terms_template")) self.ple.party_isin(
qb.from_(self.customer).where(
self.customer.payment_terms == self.filters.get("payment_terms_template")
)
)
)
if self.filters.get("sales_partner"): if self.filters.get("sales_partner"):
conditions.append("party in (select name from tabCustomer where default_sales_partner=%s)") self.qb_selection_filter.append(
values.append(self.filters.get("sales_partner")) self.ple.party_isin(
qb.from_(self.customer).where(
def add_supplier_filters(self, conditions, values): self.customer.default_sales_partner == self.filters.get("payment_terms_template")
if self.filters.get("supplier_group"): )
conditions.append( )
"""party in (select name from tabSupplier )
where supplier_group=%s)"""
def add_supplier_filters(self):
supplier = qb.DocType("Supplier")
if self.filters.get("supplier_group"):
self.qb_selection_filter.append(
self.ple.party.isin(
qb.from_(supplier)
.select(supplier.name)
.where(supplier.supplier_group == self.filters.get("supplier_group"))
)
) )
values.append(self.filters.get("supplier_group"))
if self.filters.get("payment_terms_template"): if self.filters.get("payment_terms_template"):
conditions.append("party in (select name from tabSupplier where payment_terms=%s)") self.qb_selection_filter.append(
values.append(self.filters.get("payment_terms_template")) self.ple.party.isin(
qb.from_(supplier)
.select(supplier.name)
.where(supplier.payment_terms == self.filters.get("supplier_group"))
)
)
def get_hierarchical_filters(self, doctype, key): def get_hierarchical_filters(self, doctype, key):
lft, rgt = frappe.db.get_value(doctype, self.filters.get(key), ["lft", "rgt"]) lft, rgt = frappe.db.get_value(doctype, self.filters.get(key), ["lft", "rgt"])
return """party in (select name from tabCustomer doc = qb.DocType(doctype)
where exists(select name from `tab{doctype}` where lft >= {lft} and rgt <= {rgt} ple = self.ple
and name=tabCustomer.{key}))""".format( customer = self.customer
doctype=doctype, lft=lft, rgt=rgt, key=key groups = qb.from_(doc).select(doc.name).where((doc.lft >= lft) & (doc.rgt <= rgt))
) customers = qb.from_(customer).select(customer.name).where(customer[key].isin(groups))
self.qb_selection_filter.append(ple.isin(ple.party.isin(customers)))
def add_accounting_dimensions_filters(self, conditions, values): def add_accounting_dimensions_filters(self):
accounting_dimensions = get_accounting_dimensions(as_list=False) accounting_dimensions = get_accounting_dimensions(as_list=False)
if accounting_dimensions: if accounting_dimensions:
@ -841,30 +835,16 @@ class ReceivablePayableReport(object):
self.filters[dimension.fieldname] = get_dimension_with_children( self.filters[dimension.fieldname] = get_dimension_with_children(
dimension.document_type, self.filters.get(dimension.fieldname) dimension.document_type, self.filters.get(dimension.fieldname)
) )
conditions.append("{0} in %s".format(dimension.fieldname)) self.qb_selection_filter.append(
values.append(tuple(self.filters.get(dimension.fieldname))) self.ple[dimension.fieldname].isin(self.filters[dimension.fieldname])
)
else:
self.qb_selection_filter.append(
self.ple[dimension.fieldname] == self.filters[dimension.fieldname]
)
def get_gle_balance(self, gle): def is_invoice(self, ple):
# get the balance of the GL (debit - credit) or reverse balance based on report type if ple.voucher_type in ("Sales Invoice", "Purchase Invoice"):
return gle.get(self.dr_or_cr) - self.get_reverse_balance(gle)
def get_gle_balance_in_account_currency(self, gle):
# get the balance of the GL (debit - credit) or reverse balance based on report type
return gle.get(
self.dr_or_cr + "_in_account_currency"
) - self.get_reverse_balance_in_account_currency(gle)
def get_reverse_balance_in_account_currency(self, gle):
return gle.get(
"debit_in_account_currency" if self.dr_or_cr == "credit" else "credit_in_account_currency"
)
def get_reverse_balance(self, gle):
# get "credit" balance if report type is "debit" and vice versa
return gle.get("debit" if self.dr_or_cr == "credit" else "credit")
def is_invoice(self, gle):
if gle.voucher_type in ("Sales Invoice", "Purchase Invoice"):
return True return True
def get_party_details(self, party): def get_party_details(self, party):
@ -926,9 +906,6 @@ class ReceivablePayableReport(object):
width=180, width=180,
) )
if self.filters.show_remarks:
self.add_column(label=_("Remarks"), fieldname="remarks", fieldtype="Text", width=200),
self.add_column(label="Due Date", fieldtype="Date") self.add_column(label="Due Date", fieldtype="Date")
if self.party_type == "Supplier": if self.party_type == "Supplier":

View File

@ -12,6 +12,7 @@ class TestAccountsReceivable(unittest.TestCase):
def test_accounts_receivable(self): def test_accounts_receivable(self):
frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company 2'") frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company 2'")
frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 2'") frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 2'")
frappe.db.sql("delete from `tabPayment Ledger Entry` where company='_Test Company 2'")
filters = { filters = {
"company": "_Test Company 2", "company": "_Test Company 2",

View File

@ -9,7 +9,10 @@ import frappe
import frappe.defaults import frappe.defaults
from frappe import _, qb, throw from frappe import _, qb, throw
from frappe.model.meta import get_field_precision from frappe.model.meta import get_field_precision
from frappe.query_builder.utils import DocType
from frappe.utils import cint, cstr, flt, formatdate, get_number_format_info, getdate, now, nowdate from frappe.utils import cint, cstr, flt, formatdate, get_number_format_info, getdate, now, nowdate
from pypika import Order
from pypika.terms import ExistsCriterion
import erpnext import erpnext
@ -42,37 +45,32 @@ def get_fiscal_years(
if not fiscal_years: if not fiscal_years:
# if year start date is 2012-04-01, year end date should be 2013-03-31 (hence subdate) # if year start date is 2012-04-01, year end date should be 2013-03-31 (hence subdate)
cond = "" FY = DocType("Fiscal Year")
if fiscal_year:
cond += " and fy.name = {0}".format(frappe.db.escape(fiscal_year))
if company:
cond += """
and (not exists (select name
from `tabFiscal Year Company` fyc
where fyc.parent = fy.name)
or exists(select company
from `tabFiscal Year Company` fyc
where fyc.parent = fy.name
and fyc.company=%(company)s)
)
"""
fiscal_years = frappe.db.sql( query = (
""" frappe.qb.from_(FY)
select .select(FY.name, FY.year_start_date, FY.year_end_date)
fy.name, fy.year_start_date, fy.year_end_date .where(FY.disabled == 0)
from
`tabFiscal Year` fy
where
disabled = 0 {0}
order by
fy.year_start_date desc""".format(
cond
),
{"company": company},
as_dict=True,
) )
if fiscal_year:
query = query.where(FY.name == fiscal_year)
if company:
FYC = DocType("Fiscal Year Company")
query = query.where(
ExistsCriterion(frappe.qb.from_(FYC).select(FYC.name).where(FYC.parent == FY.name)).negate()
| ExistsCriterion(
frappe.qb.from_(FYC)
.select(FYC.company)
.where(FYC.parent == FY.name)
.where(FYC.company == company)
)
)
query = query.orderby(FY.year_start_date, Order.desc)
fiscal_years = query.run(as_dict=True)
frappe.cache().hset("fiscal_years", company, fiscal_years) frappe.cache().hset("fiscal_years", company, fiscal_years)
if not transaction_date and not fiscal_year: if not transaction_date and not fiscal_year:

View File

@ -10,7 +10,7 @@ from frappe.utils.data import fmt_money
from frappe.utils.jinja import render_template from frappe.utils.jinja import render_template
from frappe.utils.pdf import get_pdf from frappe.utils.pdf import get_pdf
from frappe.utils.print_format import read_multi_pdf from frappe.utils.print_format import read_multi_pdf
from PyPDF2 import PdfFileWriter from PyPDF2 import PdfWriter
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year
@ -106,7 +106,7 @@ def irs_1099_print(filters):
columns, data = execute(filters) columns, data = execute(filters)
template = frappe.get_doc("Print Format", "IRS 1099 Form").html template = frappe.get_doc("Print Format", "IRS 1099 Form").html
output = PdfFileWriter() output = PdfWriter()
for row in data: for row in data:
row["fiscal_year"] = fiscal_year row["fiscal_year"] = fiscal_year

View File

@ -59,7 +59,6 @@ class TestExotel(FrappeAPITestCase):
f"/api/method/erpnext.erpnext_integrations.exotel_integration.{api_method}", f"/api/method/erpnext.erpnext_integrations.exotel_integration.{api_method}",
data=frappe.as_json(data), data=frappe.as_json(data),
content_type="application/json", content_type="application/json",
as_tuple=True,
) )
# restart db connection to get latest data # restart db connection to get latest data
frappe.connect() frappe.connect()

View File

@ -9908,5 +9908,5 @@ Delete Transactions,Supprimer les transactions
Default Payment Discount Account,Compte par défaut des paiements de remise Default Payment Discount Account,Compte par défaut des paiements de remise
Unrealized Profit / Loss Account,Compte de perte Unrealized Profit / Loss Account,Compte de perte
Enable Provisional Accounting For Non Stock Items,Activer la provision pour les articles non stockés Enable Provisional Accounting For Non Stock Items,Activer la provision pour les articles non stockés
Publish in Website=Publier sur le Site Web Publish in Website,Publier sur le Site Web
List View,Vue en liste List View,Vue en liste

Can't render this file because it is too large.

View File

@ -1,46 +0,0 @@
# Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import frappe
from frappe import _
from frappe.utils.bot import BotParser
class FindItemBot(BotParser):
def get_reply(self):
if self.startswith("where is", "find item", "locate"):
if not frappe.has_permission("Warehouse"):
raise frappe.PermissionError
item = "%{0}%".format(self.strip_words(self.query, "where is", "find item", "locate"))
items = frappe.db.sql(
"""select name from `tabItem` where item_code like %(txt)s
or item_name like %(txt)s or description like %(txt)s""",
dict(txt=item),
)
if items:
out = []
warehouses = frappe.get_all("Warehouse")
for item in items:
found = False
for warehouse in warehouses:
qty = frappe.db.get_value(
"Bin", {"item_code": item[0], "warehouse": warehouse.name}, "actual_qty"
)
if qty:
out.append(
_("{0} units of [{1}](/app/Form/Item/{1}) found in [{2}](/app/Form/Warehouse/{2})").format(
qty, item[0], warehouse.name
)
)
found = True
if not found:
out.append(_("[{0}](/app/Form/Item/{0}) is out of stock").format(item[0]))
return "\n\n".join(out)
else:
return _("Did not find any item called {0}").format(item)