feat(Accounting): Process Statement Of Accounts (#22901)
* feat: Process Statement Of Accounts initial commit * fix: add jinja supported inputs for subject and body in email settings * feat: utils support in template, tested autoemail, fixed issues
This commit is contained in:
parent
79d731dcd8
commit
ee5b9c7691
@ -0,0 +1,89 @@
|
||||
<h1 class="text-center" style="page-break-before:always">{{ filters.party[0] }}</h1>
|
||||
<h3 class="text-center">{{ _("Statement of Accounts") }}</h3>
|
||||
|
||||
<h5 class="text-center">
|
||||
{{ frappe.format(filters.from_date, 'Date')}}
|
||||
{{ _("to") }}
|
||||
{{ frappe.format(filters.to_date, 'Date')}}
|
||||
</h5>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 12%">{{ _("Date") }}</th>
|
||||
<th style="width: 15%">{{ _("Ref") }}</th>
|
||||
<th style="width: 25%">{{ _("Party") }}</th>
|
||||
<th style="width: 15%">{{ _("Debit") }}</th>
|
||||
<th style="width: 15%">{{ _("Credit") }}</th>
|
||||
<th style="width: 18%">{{ _("Balance (Dr - Cr)") }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in data %}
|
||||
<tr>
|
||||
{% if(row.posting_date) %}
|
||||
<td>{{ frappe.format(row.posting_date, 'Date') }}</td>
|
||||
<td>{{ row.voucher_type }}
|
||||
<br>{{ row.voucher_no }}</td>
|
||||
<td>
|
||||
{% if not (filters.party or filters.account) %}
|
||||
{{ row.party or row.account }}
|
||||
<br>
|
||||
{% endif %}
|
||||
|
||||
{{ _("Against") }}: {{ row.against }}
|
||||
<br>{{ _("Remarks") }}: {{ row.remarks }}
|
||||
{% if row.bill_no %}
|
||||
<br>{{ _("Supplier Invoice No") }}: {{ row.bill_no }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
{{ frappe.utils.fmt_money(row.debit, filters.presentation_currency) }}</td>
|
||||
<td style="text-align: right">
|
||||
{{ frappe.utils.fmt_money(row.credit, filters.presentation_currency) }}</td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td><b>{{ frappe.format(row.account, {fieldtype: "Link"}) or " " }}</b></td>
|
||||
<td style="text-align: right">
|
||||
{{ row.account and frappe.utils.fmt_money(row.debit, filters.presentation_currency) }}
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
{{ row.account and frappe.utils.fmt_money(row.credit, filters.presentation_currency) }}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td style="text-align: right">
|
||||
{{ frappe.utils.fmt_money(row.balance, filters.presentation_currency) }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<br><br>
|
||||
{% if aging %}
|
||||
<h3 class="text-center">{{ _("Ageing Report Based On ") }} {{ aging.ageing_based_on }}</h3>
|
||||
<h5 class="text-center">
|
||||
{{ _("Up to " ) }} {{ frappe.format(filters.to_date, 'Date')}}
|
||||
</h5>
|
||||
<br>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 12%">30 Days</th>
|
||||
<th style="width: 15%">60 Days</th>
|
||||
<th style="width: 25%">90 Days</th>
|
||||
<th style="width: 15%">120 Days</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ aging.range1 }}</td>
|
||||
<td>{{ aging.range2 }}</td>
|
||||
<td>{{ aging.range3 }}</td>
|
||||
<td>{{ aging.range4 }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<p class="text-right text-muted">Printed On {{ frappe.format(frappe.utils.get_datetime(), 'Datetime') }}</p>
|
@ -0,0 +1,132 @@
|
||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Process Statement Of Accounts', {
|
||||
view_properties: function(frm) {
|
||||
frappe.route_options = {doc_type: 'Customer'};
|
||||
frappe.set_route("Form", "Customize Form");
|
||||
},
|
||||
refresh: function(frm){
|
||||
if(!frm.doc.__islocal) {
|
||||
frm.add_custom_button('Send Emails',function(){
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_emails",
|
||||
args: {
|
||||
"document_name": frm.doc.name,
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r && r.message) {
|
||||
frappe.show_alert({message: __('Emails Queued'), indicator: 'blue'});
|
||||
}
|
||||
else{
|
||||
frappe.msgprint('No Records for these settings.')
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
frm.add_custom_button('Download',function(){
|
||||
var url = frappe.urllib.get_full_url(
|
||||
'/api/method/erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.download_statements?'
|
||||
+ 'document_name='+encodeURIComponent(frm.doc.name))
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'GET',
|
||||
success: function(result) {
|
||||
if(jQuery.isEmptyObject(result)){
|
||||
frappe.msgprint('No Records for these settings.');
|
||||
}
|
||||
else{
|
||||
window.location = url;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
onload: function(frm) {
|
||||
frm.set_query('currency', function(){
|
||||
return {
|
||||
filters: {
|
||||
'enabled': 1
|
||||
}
|
||||
}
|
||||
});
|
||||
if(frm.doc.__islocal){
|
||||
frm.set_value('from_date', frappe.datetime.add_months(frappe.datetime.get_today(), -1));
|
||||
frm.set_value('to_date', frappe.datetime.get_today());
|
||||
}
|
||||
},
|
||||
customer_collection: function(frm){
|
||||
frm.set_value('collection_name', '');
|
||||
if(frm.doc.customer_collection){
|
||||
frm.get_field('collection_name').set_label(frm.doc.customer_collection);
|
||||
}
|
||||
},
|
||||
frequency: function(frm){
|
||||
if(frm.doc.frequency != ''){
|
||||
frm.set_value('start_date', frappe.datetime.get_today());
|
||||
}
|
||||
else{
|
||||
frm.set_value('start_date', '');
|
||||
}
|
||||
},
|
||||
fetch_customers: function(frm){
|
||||
if(frm.doc.collection_name){
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.fetch_customers",
|
||||
args: {
|
||||
'customer_collection': frm.doc.customer_collection,
|
||||
'collection_name': frm.doc.collection_name,
|
||||
'primary_mandatory': frm.doc.primary_mandatory
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
if(r.message.length){
|
||||
frm.clear_table('customers');
|
||||
for (const customer of r.message){
|
||||
var row = frm.add_child('customers');
|
||||
row.customer = customer.name;
|
||||
row.primary_email = customer.primary_email;
|
||||
row.billing_email = customer.billing_email;
|
||||
}
|
||||
frm.refresh_field('customers');
|
||||
}
|
||||
else{
|
||||
frappe.msgprint('No Customers found with selected options.');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
frappe.throw('Enter ' + frm.doc.customer_collection + ' name.');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on('Process Statement Of Accounts Customer', {
|
||||
customer: function(frm, cdt, cdn){
|
||||
var row = locals[cdt][cdn];
|
||||
if (!row.customer){
|
||||
return;
|
||||
}
|
||||
frappe.call({
|
||||
method: 'erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.get_customer_emails',
|
||||
args: {
|
||||
'customer_name': row.customer,
|
||||
'primary_mandatory': frm.doc.primary_mandatory
|
||||
},
|
||||
callback: function(r){
|
||||
if(!r.exe){
|
||||
if(r.message.length){
|
||||
frappe.model.set_value(cdt, cdn, "primary_email", r.message[0])
|
||||
frappe.model.set_value(cdt, cdn, "billing_email", r.message[1])
|
||||
}
|
||||
else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
@ -0,0 +1,310 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_workflow": 1,
|
||||
"autoname": "Prompt",
|
||||
"creation": "2020-05-22 16:46:18.712954",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"section_break_11",
|
||||
"from_date",
|
||||
"company",
|
||||
"account",
|
||||
"group_by",
|
||||
"cost_center",
|
||||
"column_break_14",
|
||||
"to_date",
|
||||
"finance_book",
|
||||
"currency",
|
||||
"project",
|
||||
"section_break_3",
|
||||
"customer_collection",
|
||||
"collection_name",
|
||||
"fetch_customers",
|
||||
"column_break_6",
|
||||
"primary_mandatory",
|
||||
"column_break_17",
|
||||
"customers",
|
||||
"preferences",
|
||||
"orientation",
|
||||
"section_break_14",
|
||||
"include_ageing",
|
||||
"ageing_based_on",
|
||||
"section_break_1",
|
||||
"enable_auto_email",
|
||||
"section_break_18",
|
||||
"frequency",
|
||||
"filter_duration",
|
||||
"column_break_21",
|
||||
"start_date",
|
||||
"section_break_33",
|
||||
"subject",
|
||||
"column_break_28",
|
||||
"cc_to",
|
||||
"section_break_30",
|
||||
"body",
|
||||
"help_text"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "frequency",
|
||||
"fieldtype": "Select",
|
||||
"label": "Frequency",
|
||||
"options": "Weekly\nMonthly\nQuarterly"
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.enable_auto_email == 0;",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From Date",
|
||||
"mandatory_depends_on": "eval:doc.frequency == '';"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.enable_auto_email == 0;",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To Date",
|
||||
"mandatory_depends_on": "eval:doc.frequency == '';"
|
||||
},
|
||||
{
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Table MultiSelect",
|
||||
"label": "Cost Center",
|
||||
"options": "PSOA Cost Center"
|
||||
},
|
||||
{
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Table MultiSelect",
|
||||
"label": "Project",
|
||||
"options": "PSOA Project"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_3",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Customers"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_6",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_11",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "General Ledger Filters"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_14",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_17",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "customer_collection",
|
||||
"fieldtype": "Select",
|
||||
"label": "Select Customers By",
|
||||
"options": "\nCustomer Group\nTerritory\nSales Partner\nSales Person"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.customer_collection !== ''",
|
||||
"fieldname": "collection_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"label": "Recipient",
|
||||
"options": "customer_collection"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_1",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Email Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Account",
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"fieldname": "finance_book",
|
||||
"fieldtype": "Link",
|
||||
"label": "Finance Book",
|
||||
"options": "Finance Book"
|
||||
},
|
||||
{
|
||||
"fieldname": "preferences",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Print Preferences"
|
||||
},
|
||||
{
|
||||
"fieldname": "orientation",
|
||||
"fieldtype": "Select",
|
||||
"label": "Orientation",
|
||||
"options": "Landscape\nPortrait"
|
||||
},
|
||||
{
|
||||
"default": "Today",
|
||||
"fieldname": "start_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Start Date"
|
||||
},
|
||||
{
|
||||
"default": "Group by Voucher (Consolidated)",
|
||||
"fieldname": "group_by",
|
||||
"fieldtype": "Select",
|
||||
"label": "Group By",
|
||||
"options": "\nGroup by Voucher\nGroup by Voucher (Consolidated)"
|
||||
},
|
||||
{
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
"label": "Currency",
|
||||
"options": "Currency"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "include_ageing",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Include Ageing Summary"
|
||||
},
|
||||
{
|
||||
"default": "Due Date",
|
||||
"depends_on": "eval:doc.include_ageing === 1",
|
||||
"fieldname": "ageing_based_on",
|
||||
"fieldtype": "Select",
|
||||
"label": "Ageing Based On",
|
||||
"options": "Due Date\nPosting Date"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "enable_auto_email",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Enable Auto Email"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_14",
|
||||
"fieldtype": "Column Break",
|
||||
"hide_border": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.enable_auto_email ==1",
|
||||
"fieldname": "section_break_18",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_21",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.customer_collection !== ''",
|
||||
"fieldname": "fetch_customers",
|
||||
"fieldtype": "Button",
|
||||
"label": "Fetch Customers",
|
||||
"options": "fetch_customers",
|
||||
"print_hide": 1,
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "primary_mandatory",
|
||||
"fieldtype": "Check",
|
||||
"label": "Send To Primary Contact"
|
||||
},
|
||||
{
|
||||
"fieldname": "cc_to",
|
||||
"fieldtype": "Link",
|
||||
"label": "CC To",
|
||||
"options": "User"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "filter_duration",
|
||||
"fieldtype": "Int",
|
||||
"label": "Filter Duration (Months)"
|
||||
},
|
||||
{
|
||||
"fieldname": "customers",
|
||||
"fieldtype": "Table",
|
||||
"label": "Customers",
|
||||
"options": "Process Statement Of Accounts Customer",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_28",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_30",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_33",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "help_text",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Help Text",
|
||||
"options": "<br>\n<h4>Note</h4>\n<ul>\n<li>\nYou can use <a href=\"https://jinja.palletsprojects.com/en/2.11.x/\" target=\"_blank\">Jinja tags</a> in <b>Subject</b> and <b>Body</b> fields for dynamic values.\n</li><li>\n All fields in this doctype are available under the <b>doc</b> object and all fields for the customer to whom the mail will go to is available under the <b>customer</b> object.\n</li></ul>\n<h4> Examples</h4>\n<!-- {% raw %} -->\n<ul>\n <li><b>Subject</b>:<br><br><pre><code>Statement Of Accounts for {{ customer.name }}</code></pre><br></li>\n <li><b>Body</b>: <br><br>\n<pre><code>Hello {{ customer.name }},<br>PFA your Statement Of Accounts from {{ doc.from_date }} to {{ doc.to_date }}.</code> </pre></li>\n</ul>\n<!-- {% endraw %} -->"
|
||||
},
|
||||
{
|
||||
"fieldname": "subject",
|
||||
"fieldtype": "Data",
|
||||
"label": "Subject"
|
||||
},
|
||||
{
|
||||
"fieldname": "body",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Body"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-08-08 08:47:09.185728",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Process Statement Of Accounts",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts User",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -0,0 +1,271 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from erpnext.accounts.report.general_ledger.general_ledger import execute as get_soa
|
||||
from erpnext.accounts.report.accounts_receivable_summary.accounts_receivable_summary import execute as get_ageing
|
||||
from frappe.core.doctype.communication.email import make
|
||||
|
||||
from frappe.utils.print_format import report_to_pdf
|
||||
from frappe.utils.pdf import get_pdf
|
||||
from frappe.utils import today, add_days, add_months, getdate, format_date
|
||||
from frappe.utils.jinja import validate_template
|
||||
|
||||
import copy
|
||||
from datetime import timedelta
|
||||
from frappe.www.printview import get_print_style
|
||||
|
||||
class ProcessStatementOfAccounts(Document):
|
||||
def validate(self):
|
||||
if not self.subject:
|
||||
self.subject = 'Statement Of Accounts for {{ customer.name }}'
|
||||
if not self.body:
|
||||
self.body = 'Hello {{ customer.name }},<br>PFA your Statement Of Accounts from {{ doc.from_date }} to {{ doc.to_date }}.'
|
||||
|
||||
validate_template(self.subject)
|
||||
validate_template(self.body)
|
||||
|
||||
if not self.customers:
|
||||
frappe.throw(frappe._('Customers not selected.'))
|
||||
|
||||
if self.enable_auto_email:
|
||||
self.to_date = self.start_date
|
||||
self.from_date = add_months(self.to_date, -1 * self.filter_duration)
|
||||
|
||||
|
||||
def get_report_pdf(doc, consolidated=True):
|
||||
statement_dict = {}
|
||||
aging = ''
|
||||
base_template_path = "frappe/www/printview.html"
|
||||
template_path = "erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html"
|
||||
|
||||
for entry in doc.customers:
|
||||
if doc.include_ageing:
|
||||
ageing_filters = frappe._dict({
|
||||
'company': doc.company,
|
||||
'report_date': doc.to_date,
|
||||
'ageing_based_on': doc.ageing_based_on,
|
||||
'range1': 30,
|
||||
'range2': 60,
|
||||
'range3': 90,
|
||||
'range4': 120,
|
||||
'customer': entry.customer
|
||||
})
|
||||
col1, aging = get_ageing(ageing_filters)
|
||||
aging[0]['ageing_based_on'] = doc.ageing_based_on
|
||||
|
||||
tax_id = frappe.get_doc('Customer', entry.customer).tax_id
|
||||
|
||||
filters= frappe._dict({
|
||||
'from_date': doc.from_date,
|
||||
'to_date': doc.to_date,
|
||||
'company': doc.company,
|
||||
'finance_book': doc.finance_book if doc.finance_book else None,
|
||||
"account": doc.account if doc.account else None,
|
||||
'party_type': 'Customer',
|
||||
'party': [entry.customer],
|
||||
'group_by': doc.group_by,
|
||||
'currency': doc.currency,
|
||||
'cost_center': [cc.cost_center_name for cc in doc.cost_center],
|
||||
'project': [p.project_name for p in doc.project],
|
||||
'show_opening_entries': 0,
|
||||
'include_default_book_entries': 0,
|
||||
'show_cancelled_entries': 1,
|
||||
'tax_id': tax_id if tax_id else None
|
||||
})
|
||||
col, res = get_soa(filters)
|
||||
|
||||
for x in [0, -2, -1]:
|
||||
res[x]['account'] = res[x]['account'].replace("'","")
|
||||
|
||||
if len(res) == 3:
|
||||
continue
|
||||
html = frappe.render_template(template_path, \
|
||||
{"filters": filters, "data": res, "aging": aging[0] if doc.include_ageing else None})
|
||||
html = frappe.render_template(base_template_path, {"body": html, \
|
||||
"css": get_print_style(), "title": "Statement For " + entry.customer})
|
||||
statement_dict[entry.customer] = html
|
||||
if not bool(statement_dict):
|
||||
return False
|
||||
elif consolidated:
|
||||
result = ''.join(list(statement_dict.values()))
|
||||
return get_pdf(result, {'orientation': doc.orientation})
|
||||
else:
|
||||
for customer, statement_html in statement_dict.items():
|
||||
statement_dict[customer]=get_pdf(statement_html, {'orientation': doc.orientation})
|
||||
return statement_dict
|
||||
|
||||
def get_customers_based_on_territory_or_customer_group(customer_collection, collection_name):
|
||||
fields_dict = {
|
||||
'Customer Group': 'customer_group',
|
||||
'Territory': 'territory',
|
||||
}
|
||||
collection = frappe.get_doc(customer_collection, collection_name)
|
||||
selected = [customer.name for customer in frappe.get_list(customer_collection, filters=[
|
||||
['lft', '>=', collection.lft],
|
||||
['rgt', '<=', collection.rgt]
|
||||
],
|
||||
fields=['name'],
|
||||
order_by='lft asc, rgt desc'
|
||||
)]
|
||||
return frappe.get_list('Customer', fields=['name', 'email_id'], \
|
||||
filters=[[fields_dict[customer_collection], 'IN', selected]])
|
||||
|
||||
def get_customers_based_on_sales_person(sales_person):
|
||||
lft, rgt = frappe.db.get_value("Sales Person",
|
||||
sales_person, ["lft", "rgt"])
|
||||
records = frappe.db.sql("""
|
||||
select distinct parent, parenttype
|
||||
from `tabSales Team` steam
|
||||
where parenttype = 'Customer'
|
||||
and exists(select name from `tabSales Person` where lft >= %s and rgt <= %s and name = steam.sales_person)
|
||||
""", (lft, rgt), as_dict=1)
|
||||
sales_person_records = frappe._dict()
|
||||
for d in records:
|
||||
sales_person_records.setdefault(d.parenttype, set()).add(d.parent)
|
||||
customers = frappe.get_list('Customer', fields=['name', 'email_id'], \
|
||||
filters=[['name', 'in', list(sales_person_records['Customer'])]])
|
||||
return customers
|
||||
|
||||
def get_recipients_and_cc(customer, doc):
|
||||
recipients = []
|
||||
for clist in doc.customers:
|
||||
if clist.customer == customer:
|
||||
recipients.append(clist.billing_email)
|
||||
if doc.primary_mandatory and clist.primary_email:
|
||||
recipients.append(clist.primary_email)
|
||||
cc = []
|
||||
if doc.cc_to != '':
|
||||
try:
|
||||
cc=[frappe.get_value('User', doc.cc_to, 'email')]
|
||||
except:
|
||||
pass
|
||||
|
||||
return recipients, cc
|
||||
|
||||
def get_context(customer, doc):
|
||||
template_doc = copy.deepcopy(doc)
|
||||
del template_doc.customers
|
||||
template_doc.from_date = format_date(template_doc.from_date)
|
||||
template_doc.to_date = format_date(template_doc.to_date)
|
||||
return {
|
||||
'doc': template_doc,
|
||||
'customer': frappe.get_doc('Customer', customer),
|
||||
'frappe': frappe.utils
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def fetch_customers(customer_collection, collection_name, primary_mandatory):
|
||||
customer_list = []
|
||||
customers = []
|
||||
|
||||
if customer_collection == 'Sales Person':
|
||||
customers = get_customers_based_on_sales_person(collection_name)
|
||||
if not bool(customers):
|
||||
frappe.throw('No Customers found with selected options.')
|
||||
else:
|
||||
if customer_collection == 'Sales Partner':
|
||||
customers = frappe.get_list('Customer', fields=['name', 'email_id'], \
|
||||
filters=[['default_sales_partner', '=', collection_name]])
|
||||
else:
|
||||
customers = get_customers_based_on_territory_or_customer_group(customer_collection, collection_name)
|
||||
|
||||
for customer in customers:
|
||||
primary_email = customer.get('email_id') or ''
|
||||
billing_email = get_customer_emails(customer.name, 1, billing_and_primary=False)
|
||||
|
||||
if billing_email == '' or (primary_email == '' and int(primary_mandatory)):
|
||||
continue
|
||||
|
||||
customer_list.append({
|
||||
'name': customer.name,
|
||||
'primary_email': primary_email,
|
||||
'billing_email': billing_email
|
||||
})
|
||||
return customer_list
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_customer_emails(customer_name, primary_mandatory, billing_and_primary=True):
|
||||
billing_email = frappe.db.sql("""
|
||||
SELECT c.email_id FROM `tabContact` AS c JOIN `tabDynamic Link` AS l ON c.name=l.parent \
|
||||
WHERE l.link_doctype='Customer' and l.link_name='""" + customer_name + """' and \
|
||||
c.is_billing_contact=1 \
|
||||
order by c.creation desc""")
|
||||
|
||||
if len(billing_email) == 0 or (billing_email[0][0] is None):
|
||||
if billing_and_primary:
|
||||
frappe.throw('No billing email found for customer: '+ customer_name)
|
||||
else:
|
||||
return ''
|
||||
|
||||
if billing_and_primary:
|
||||
primary_email = frappe.get_value('Customer', customer_name, 'email_id')
|
||||
if primary_email is None and int(primary_mandatory):
|
||||
frappe.throw('No primary email found for customer: '+ customer_name)
|
||||
return [primary_email or '', billing_email[0][0]]
|
||||
else:
|
||||
return billing_email[0][0] or ''
|
||||
|
||||
@frappe.whitelist()
|
||||
def download_statements(document_name):
|
||||
doc = frappe.get_doc('Process Statement Of Accounts', document_name)
|
||||
report = get_report_pdf(doc)
|
||||
if report:
|
||||
frappe.local.response.filename = doc.name + '.pdf'
|
||||
frappe.local.response.filecontent = report
|
||||
frappe.local.response.type = "download"
|
||||
|
||||
@frappe.whitelist()
|
||||
def send_emails(document_name, from_scheduler=False):
|
||||
doc = frappe.get_doc('Process Statement Of Accounts', document_name)
|
||||
report = get_report_pdf(doc, consolidated=False)
|
||||
|
||||
if report:
|
||||
for customer, report_pdf in report.items():
|
||||
attachments = [{
|
||||
'fname': customer + '.pdf',
|
||||
'fcontent': report_pdf
|
||||
}]
|
||||
|
||||
recipients, cc = get_recipients_and_cc(customer, doc)
|
||||
context = get_context(customer, doc)
|
||||
subject = frappe.render_template(doc.subject, context)
|
||||
message = frappe.render_template(doc.body, context)
|
||||
|
||||
frappe.enqueue(
|
||||
queue='short',
|
||||
method=frappe.sendmail,
|
||||
recipients=recipients,
|
||||
sender=frappe.session.user,
|
||||
cc=cc,
|
||||
subject=subject,
|
||||
message=message,
|
||||
now=True,
|
||||
reference_doctype='Process Statement Of Accounts',
|
||||
reference_name=document_name,
|
||||
attachments=attachments
|
||||
)
|
||||
|
||||
if doc.enable_auto_email and from_scheduler:
|
||||
new_to_date = getdate(today())
|
||||
if doc.frequency == 'Weekly':
|
||||
new_to_date = add_days(new_to_date, 7)
|
||||
else:
|
||||
new_to_date = add_months(new_to_date, 1 if doc.frequency == 'Monthly' else 3)
|
||||
new_from_date = add_months(new_to_date, -1 * doc.filter_duration)
|
||||
doc.add_comment('Comment', 'Emails sent on: ' + frappe.utils.format_datetime(frappe.utils.now()))
|
||||
doc.db_set('to_date', new_to_date, commit=True)
|
||||
doc.db_set('from_date', new_from_date, commit=True)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@frappe.whitelist()
|
||||
def send_auto_email():
|
||||
selected = frappe.get_list('Process Statement Of Accounts', filters={'to_date': format_date(today()), 'enable_auto_email': 1})
|
||||
for entry in selected:
|
||||
send_emails(entry.name, from_scheduler=True)
|
||||
return True
|
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestProcessStatementOfAccounts(unittest.TestCase):
|
||||
pass
|
@ -0,0 +1,47 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_workflow": 1,
|
||||
"creation": "2020-08-03 16:35:21.852178",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"customer",
|
||||
"billing_email",
|
||||
"primary_email"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Customer",
|
||||
"options": "Customer",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "primary_email",
|
||||
"fieldtype": "Read Only",
|
||||
"in_list_view": 1,
|
||||
"label": "Primary Contact Email"
|
||||
},
|
||||
{
|
||||
"fieldname": "billing_email",
|
||||
"fieldtype": "Read Only",
|
||||
"in_list_view": 1,
|
||||
"label": "Billing Email"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-08-03 22:55:38.875601",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Process Statement Of Accounts Customer",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class ProcessStatementOfAccountsCustomer(Document):
|
||||
pass
|
@ -0,0 +1,30 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2020-08-03 16:56:45.744905",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"cost_center_name"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "cost_center_name",
|
||||
"fieldtype": "Link",
|
||||
"label": "Cost Center",
|
||||
"options": "Cost Center"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-08-03 16:56:45.744905",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "PSOA Cost Center",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class PSOACostCenter(Document):
|
||||
pass
|
0
erpnext/accounts/doctype/psoa_project/__init__.py
Normal file
0
erpnext/accounts/doctype/psoa_project/__init__.py
Normal file
30
erpnext/accounts/doctype/psoa_project/psoa_project.json
Normal file
30
erpnext/accounts/doctype/psoa_project/psoa_project.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2020-08-03 16:52:14.731978",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"project_name"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "project_name",
|
||||
"fieldtype": "Link",
|
||||
"label": "Project",
|
||||
"options": "Project"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-08-03 16:53:39.219736",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "PSOA Project",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
10
erpnext/accounts/doctype/psoa_project/psoa_project.py
Normal file
10
erpnext/accounts/doctype/psoa_project/psoa_project.py
Normal file
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class PSOAProject(Document):
|
||||
pass
|
@ -322,7 +322,8 @@ scheduler_events = {
|
||||
"erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status",
|
||||
"erpnext.selling.doctype.quotation.quotation.set_expired_status",
|
||||
"erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_appointment_status",
|
||||
"erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status"
|
||||
"erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status",
|
||||
"erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_auto_email"
|
||||
],
|
||||
"daily_long": [
|
||||
"erpnext.setup.doctype.email_digest.email_digest.send",
|
||||
|
Loading…
x
Reference in New Issue
Block a user