Merge branch 'version-13-hotfix' into hsn-summary-report

This commit is contained in:
Deepesh Garg
2022-11-15 09:26:00 +05:30
committed by GitHub
8 changed files with 106 additions and 75 deletions

View File

@@ -43,20 +43,13 @@ frappe.ui.form.on('Bank Guarantee', {
reference_docname: function(frm) { reference_docname: function(frm) {
if (frm.doc.reference_docname && frm.doc.reference_doctype) { if (frm.doc.reference_docname && frm.doc.reference_doctype) {
let fields_to_fetch = ["grand_total"];
let party_field = frm.doc.reference_doctype == "Sales Order" ? "customer" : "supplier"; let party_field = frm.doc.reference_doctype == "Sales Order" ? "customer" : "supplier";
if (frm.doc.reference_doctype == "Sales Order") {
fields_to_fetch.push("project");
}
fields_to_fetch.push(party_field);
frappe.call({ frappe.call({
method: "erpnext.accounts.doctype.bank_guarantee.bank_guarantee.get_vouchar_detials", method: "erpnext.accounts.doctype.bank_guarantee.bank_guarantee.get_voucher_details",
args: { args: {
"column_list": fields_to_fetch, "bank_guarantee_type": frm.doc.bg_type,
"doctype": frm.doc.reference_doctype, "reference_name": frm.doc.reference_docname
"docname": frm.doc.reference_docname
}, },
callback: function(r) { callback: function(r) {
if (r.message) { if (r.message) {

View File

@@ -2,11 +2,8 @@
# For license information, please see license.txt # For license information, please see license.txt
import json
import frappe import frappe
from frappe import _ from frappe import _
from frappe.desk.search import sanitize_searchfield
from frappe.model.document import Document from frappe.model.document import Document
@@ -25,14 +22,18 @@ class BankGuarantee(Document):
@frappe.whitelist() @frappe.whitelist()
def get_vouchar_detials(column_list, doctype, docname): def get_voucher_details(bank_guarantee_type: str, reference_name: str):
column_list = json.loads(column_list) if not isinstance(reference_name, str):
for col in column_list: raise TypeError("reference_name must be a string")
sanitize_searchfield(col)
return frappe.db.sql( fields_to_fetch = ["grand_total"]
""" select {columns} from `tab{doctype}` where name=%s""".format(
columns=", ".join(column_list), doctype=doctype if bank_guarantee_type == "Receiving":
), doctype = "Sales Order"
docname, fields_to_fetch.append("customer")
as_dict=1, fields_to_fetch.append("project")
)[0] else:
doctype = "Purchase Order"
fields_to_fetch.append("supplier")
return frappe.db.get_value(doctype, reference_name, fields_to_fetch, as_dict=True)

View File

@@ -47,6 +47,10 @@ class PaymentReconciliation(Document):
def get_payment_entries(self): def get_payment_entries(self):
order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order" order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order"
condition = self.get_conditions(get_payments=True) condition = self.get_conditions(get_payments=True)
if self.get("cost_center"):
condition += " and cost_center = '{0}' ".format(self.cost_center)
payment_entries = get_advance_payment_entries( payment_entries = get_advance_payment_entries(
self.party_type, self.party_type,
self.party, self.party,
@@ -61,6 +65,10 @@ class PaymentReconciliation(Document):
def get_jv_entries(self): def get_jv_entries(self):
condition = self.get_conditions() condition = self.get_conditions()
if self.get("cost_center"):
condition += " and t2.cost_center = '{0}' ".format(self.cost_center)
dr_or_cr = ( dr_or_cr = (
"credit_in_account_currency" "credit_in_account_currency"
if erpnext.get_party_account_type(self.party_type) == "Receivable" if erpnext.get_party_account_type(self.party_type) == "Receivable"
@@ -113,6 +121,10 @@ class PaymentReconciliation(Document):
def get_dr_or_cr_notes(self): def get_dr_or_cr_notes(self):
condition = self.get_conditions(get_return_invoices=True) condition = self.get_conditions(get_return_invoices=True)
if self.get("cost_center"):
condition += " and doc.cost_center = '{0}' ".format(self.cost_center)
dr_or_cr = ( dr_or_cr = (
"credit_in_account_currency" "credit_in_account_currency"
if erpnext.get_party_account_type(self.party_type) == "Receivable" if erpnext.get_party_account_type(self.party_type) == "Receivable"
@@ -172,6 +184,9 @@ class PaymentReconciliation(Document):
condition = self.get_conditions(get_invoices=True) condition = self.get_conditions(get_invoices=True)
if self.get("cost_center"):
condition += " and cost_center = '{0}' ".format(self.cost_center)
non_reconciled_invoices = get_outstanding_invoices( non_reconciled_invoices = get_outstanding_invoices(
self.party_type, self.party, self.receivable_payable_account, condition=condition self.party_type, self.party, self.receivable_payable_account, condition=condition
) )
@@ -357,9 +372,6 @@ class PaymentReconciliation(Document):
def get_conditions(self, get_invoices=False, get_payments=False, get_return_invoices=False): def get_conditions(self, get_invoices=False, get_payments=False, get_return_invoices=False):
condition = " and company = '{0}' ".format(self.company) condition = " and company = '{0}' ".format(self.company)
if self.get("cost_center"):
condition = " and cost_center = '{0}' ".format(self.cost_center)
if get_invoices: if get_invoices:
condition += ( condition += (
" and posting_date >= {0}".format(frappe.db.escape(self.from_invoice_date)) " and posting_date >= {0}".format(frappe.db.escape(self.from_invoice_date))

View File

@@ -8,7 +8,8 @@ frappe.ui.form.on('Process Statement Of Accounts', {
}, },
refresh: function(frm){ refresh: function(frm){
if(!frm.doc.__islocal) { if(!frm.doc.__islocal) {
frm.add_custom_button('Send Emails',function(){ frm.add_custom_button(__('Send Emails'), function(){
if (frm.is_dirty()) frappe.throw(__("Please save before proceeding."))
frappe.call({ frappe.call({
method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_emails", method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_emails",
args: { args: {
@@ -24,8 +25,9 @@ frappe.ui.form.on('Process Statement Of Accounts', {
} }
}); });
}); });
frm.add_custom_button('Download',function(){ frm.add_custom_button(__('Download'), function(){
var url = frappe.urllib.get_full_url( if (frm.is_dirty()) frappe.throw(__("Please save before proceeding."))
let url = frappe.urllib.get_full_url(
'/api/method/erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.download_statements?' '/api/method/erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.download_statements?'
+ 'document_name='+encodeURIComponent(frm.doc.name)) + 'document_name='+encodeURIComponent(frm.doc.name))
$.ajax({ $.ajax({

View File

@@ -27,6 +27,7 @@
"customers", "customers",
"preferences", "preferences",
"orientation", "orientation",
"include_break",
"include_ageing", "include_ageing",
"ageing_based_on", "ageing_based_on",
"section_break_14", "section_break_14",
@@ -284,10 +285,16 @@
"fieldtype": "Link", "fieldtype": "Link",
"label": "Terms and Conditions", "label": "Terms and Conditions",
"options": "Terms and Conditions" "options": "Terms and Conditions"
},
{
"default": "1",
"fieldname": "include_break",
"fieldtype": "Check",
"label": "Page Break After Each SoA"
} }
], ],
"links": [], "links": [],
"modified": "2021-09-06 21:00:45.732505", "modified": "2022-10-17 17:47:08.662475",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Process Statement Of Accounts", "name": "Process Statement Of Accounts",
@@ -320,5 +327,6 @@
], ],
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@@ -6,6 +6,7 @@ import copy
import frappe import frappe
from frappe import _ from frappe import _
from frappe.desk.reportview import get_match_cond
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import add_days, add_months, format_date, getdate, today from frappe.utils import add_days, add_months, format_date, getdate, today
from frappe.utils.jinja import validate_template from frappe.utils.jinja import validate_template
@@ -128,7 +129,8 @@ def get_report_pdf(doc, consolidated=True):
if not bool(statement_dict): if not bool(statement_dict):
return False return False
elif consolidated: elif consolidated:
result = "".join(list(statement_dict.values())) delimiter = '<div style="page-break-before: always;"></div>' if doc.include_break else ""
result = delimiter.join(list(statement_dict.values()))
return get_pdf(result, {"orientation": doc.orientation}) return get_pdf(result, {"orientation": doc.orientation})
else: else:
for customer, statement_html in statement_dict.items(): for customer, statement_html in statement_dict.items():
@@ -240,8 +242,6 @@ def fetch_customers(customer_collection, collection_name, primary_mandatory):
if int(primary_mandatory): if int(primary_mandatory):
if primary_email == "": if primary_email == "":
continue continue
elif (billing_email == "") and (primary_email == ""):
continue
customer_list.append( customer_list.append(
{"name": customer.name, "primary_email": primary_email, "billing_email": billing_email} {"name": customer.name, "primary_email": primary_email, "billing_email": billing_email}
@@ -273,8 +273,12 @@ def get_customer_emails(customer_name, primary_mandatory, billing_and_primary=Tr
link.link_doctype='Customer' link.link_doctype='Customer'
and link.link_name=%s and link.link_name=%s
and contact.is_billing_contact=1 and contact.is_billing_contact=1
{mcond}
ORDER BY ORDER BY
contact.creation desc""", contact.creation desc
""".format(
mcond=get_match_cond("Contact")
),
customer_name, customer_name,
) )
@@ -313,6 +317,8 @@ def send_emails(document_name, from_scheduler=False):
attachments = [{"fname": customer + ".pdf", "fcontent": report_pdf}] attachments = [{"fname": customer + ".pdf", "fcontent": report_pdf}]
recipients, cc = get_recipients_and_cc(customer, doc) recipients, cc = get_recipients_and_cc(customer, doc)
if not recipients:
continue
context = get_context(customer, doc) context = get_context(customer, doc)
subject = frappe.render_template(doc.subject, context) subject = frappe.render_template(doc.subject, context)
message = frappe.render_template(doc.body, context) message = frappe.render_template(doc.body, context)

View File

@@ -7,6 +7,8 @@ import frappe
from frappe import _, bold from frappe import _, bold
from frappe.model.document import Document from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from frappe.query_builder import Criterion
from frappe.query_builder.functions import IfNull, Max, Min
from frappe.utils import ( from frappe.utils import (
add_days, add_days,
add_to_date, add_to_date,
@@ -112,43 +114,44 @@ class JobCard(Document):
def get_overlap_for(self, args, check_next_available_slot=False): def get_overlap_for(self, args, check_next_available_slot=False):
production_capacity = 1 production_capacity = 1
jc = frappe.qb.DocType("Job Card")
jctl = frappe.qb.DocType("Job Card Time Log")
time_conditions = [
((jctl.from_time < args.from_time) & (jctl.to_time > args.from_time)),
((jctl.from_time < args.to_time) & (jctl.to_time > args.to_time)),
((jctl.from_time >= args.from_time) & (jctl.to_time <= args.to_time)),
]
if check_next_available_slot:
time_conditions.append(((jctl.from_time >= args.from_time) & (jctl.to_time >= args.to_time)))
query = (
frappe.qb.from_(jctl)
.from_(jc)
.select(jc.name.as_("name"), jctl.to_time)
.where(
(jctl.parent == jc.name)
& (Criterion.any(time_conditions))
& (jctl.name != f"{args.name or 'No Name'}")
& (jc.name != f"{args.parent or 'No Name'}")
& (jc.docstatus < 2)
)
.orderby(jctl.to_time, order=frappe.qb.desc)
)
if self.workstation: if self.workstation:
production_capacity = ( production_capacity = (
frappe.get_cached_value("Workstation", self.workstation, "production_capacity") or 1 frappe.get_cached_value("Workstation", self.workstation, "production_capacity") or 1
) )
validate_overlap_for = " and jc.workstation = %(workstation)s " query = query.where(jc.workstation == self.workstation)
if args.get("employee"): if args.get("employee"):
# override capacity for employee # override capacity for employee
production_capacity = 1 production_capacity = 1
validate_overlap_for = " and jctl.employee = %(employee)s " query = query.where(jctl.employee == args.get("employee"))
extra_cond = "" existing = query.run(as_dict=True)
if check_next_available_slot:
extra_cond = " or (%(from_time)s <= jctl.from_time and %(to_time)s <= jctl.to_time)"
existing = frappe.db.sql(
"""select jc.name as name, jctl.to_time from
`tabJob Card Time Log` jctl, `tabJob Card` jc where jctl.parent = jc.name and
(
(%(from_time)s > jctl.from_time and %(from_time)s < jctl.to_time) or
(%(to_time)s > jctl.from_time and %(to_time)s < jctl.to_time) or
(%(from_time)s <= jctl.from_time and %(to_time)s >= jctl.to_time) {0}
)
and jctl.name != %(name)s and jc.name != %(parent)s and jc.docstatus < 2 {1}
order by jctl.to_time desc""".format(
extra_cond, validate_overlap_for
),
{
"from_time": args.from_time,
"to_time": args.to_time,
"name": args.name or "No Name",
"parent": args.parent or "No Name",
"employee": args.get("employee"),
"workstation": self.workstation,
},
as_dict=True,
)
if existing and production_capacity > len(existing): if existing and production_capacity > len(existing):
return return
@@ -488,18 +491,21 @@ class JobCard(Document):
) )
def update_work_order_data(self, for_quantity, time_in_mins, wo): def update_work_order_data(self, for_quantity, time_in_mins, wo):
time_data = frappe.db.sql( jc = frappe.qb.DocType("Job Card")
""" jctl = frappe.qb.DocType("Job Card Time Log")
SELECT
min(from_time) as start_time, max(to_time) as end_time time_data = (
FROM `tabJob Card` jc, `tabJob Card Time Log` jctl frappe.qb.from_(jc)
WHERE .from_(jctl)
jctl.parent = jc.name and jc.work_order = %s and jc.operation_id = %s .select(Min(jctl.from_time).as_("start_time"), Max(jctl.to_time).as_("end_time"))
and jc.docstatus = 1 and IFNULL(jc.is_corrective_job_card, 0) = 0 .where(
""", (jctl.parent == jc.name)
(self.work_order, self.operation_id), & (jc.work_order == self.work_order)
as_dict=1, & (jc.operation_id == self.operation_id)
) & (jc.docstatus == 1)
& (IfNull(jc.is_corrective_job_card, 0) == 0)
)
).run(as_dict=True)
for data in wo.operations: for data in wo.operations:
if data.get("name") == self.operation_id: if data.get("name") == self.operation_id:

View File

@@ -315,6 +315,9 @@ def get_basic_details(args, item, overwrite_warehouse=True):
else: else:
args.uom = item.stock_uom args.uom = item.stock_uom
# Set stock UOM in args, so that it can be used while fetching item price
args.stock_uom = item.stock_uom
if args.get("batch_no") and item.name != frappe.get_cached_value( if args.get("batch_no") and item.name != frappe.get_cached_value(
"Batch", args.get("batch_no"), "item" "Batch", args.get("batch_no"), "item"
): ):