Merge branch 'develop' into item-group-filter-portal
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index a09face..409c15f 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -638,20 +638,12 @@
return { filters: filters };
},
- reverse_journal_entry: function(frm) {
- var me = frm.doc;
- for(var i=0; i<me.accounts.length; i++) {
- me.accounts[i].credit += me.accounts[i].debit;
- me.accounts[i].debit = me.accounts[i].credit - me.accounts[i].debit;
- me.accounts[i].credit -= me.accounts[i].debit;
- me.accounts[i].credit_in_account_currency = me.accounts[i].credit;
- me.accounts[i].debit_in_account_currency = me.accounts[i].debit;
- me.accounts[i].reference_type = "Journal Entry";
- me.accounts[i].reference_name = me.name
- }
- frm.copy_doc();
- cur_frm.reload_doc();
- }
+ reverse_journal_entry: function() {
+ frappe.model.open_mapped_doc({
+ method: "erpnext.accounts.doctype.journal_entry.journal_entry.make_reverse_journal_entry",
+ frm: cur_frm
+ })
+ },
});
$.extend(erpnext.journal_entry, {
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index dda1708..0a385d0 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -1021,3 +1021,34 @@
journal_entry.posting_date = nowdate()
journal_entry.inter_company_journal_entry_reference = name
return journal_entry.as_dict()
+
+@frappe.whitelist()
+def make_reverse_journal_entry(source_name, target_doc=None, ignore_permissions=False):
+ from frappe.model.mapper import get_mapped_doc
+
+ def update_accounts(source, target, source_parent):
+ target.reference_type = "Journal Entry"
+ target.reference_name = source_parent.name
+
+ doclist = get_mapped_doc("Journal Entry", source_name, {
+ "Journal Entry": {
+ "doctype": "Journal Entry",
+ "validation": {
+ "docstatus": ["=", 1]
+ }
+ },
+ "Journal Entry Account": {
+ "doctype": "Journal Entry Account",
+ "field_map": {
+ "account_currency": "account_currency",
+ "exchange_rate": "exchange_rate",
+ "debit_in_account_currency": "credit_in_account_currency",
+ "debit": "credit",
+ "credit_in_account_currency": "debit_in_account_currency",
+ "credit": "debit",
+ },
+ "postprocess": update_accounts,
+ },
+ }, target_doc, ignore_permissions=ignore_permissions)
+
+ return doclist
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
index 479d4b6..53c0758 100644
--- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
@@ -167,6 +167,49 @@
self.assertFalse(gle)
+ def test_reverse_journal_entry(self):
+ from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
+ jv = make_journal_entry("_Test Bank USD - _TC",
+ "Sales - _TC", 100, exchange_rate=50, save=False)
+
+ jv.get("accounts")[1].credit_in_account_currency = 5000
+ jv.get("accounts")[1].exchange_rate = 1
+ jv.submit()
+
+ rjv = make_reverse_journal_entry(jv.name)
+ rjv.posting_date = nowdate()
+ rjv.submit()
+
+
+ gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
+ debit_in_account_currency, credit_in_account_currency
+ from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
+ order by account asc""", rjv.name, as_dict=1)
+
+ self.assertTrue(gl_entries)
+
+
+ expected_values = {
+ "_Test Bank USD - _TC": {
+ "account_currency": "USD",
+ "debit": 0,
+ "debit_in_account_currency": 0,
+ "credit": 5000,
+ "credit_in_account_currency": 100,
+ },
+ "Sales - _TC": {
+ "account_currency": "INR",
+ "debit": 5000,
+ "debit_in_account_currency": 5000,
+ "credit": 0,
+ "credit_in_account_currency": 0,
+ }
+ }
+
+ for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
+ for i, gle in enumerate(gl_entries):
+ self.assertEqual(expected_values[gle.account][field], gle[field])
+
def test_disallow_change_in_account_currency_for_a_party(self):
# create jv in USD
jv = make_journal_entry("_Test Bank USD - _TC",
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index cf3deb8..01d3903 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -45,8 +45,8 @@
}, as_dict=1)
if accounting_periods:
- frappe.throw(_("You can't create accounting entries in the closed accounting period {0}")
- .format(accounting_periods[0].name), ClosedAccountingPeriod)
+ frappe.throw(_("You cannot create or cancel any accounting entries with in the closed Accounting Period {0}")
+ .format(frappe.bold(accounting_periods[0].name)), ClosedAccountingPeriod)
def process_gl_map(gl_map, merge_entries=True):
if merge_entries:
@@ -301,8 +301,9 @@
})
if gl_entries:
- set_as_cancel(gl_entries[0]['voucher_type'], gl_entries[0]['voucher_no'])
+ validate_accounting_period(gl_entries)
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
+ set_as_cancel(gl_entries[0]['voucher_type'], gl_entries[0]['voucher_no'])
for entry in gl_entries:
entry['name'] = None
@@ -342,7 +343,7 @@
"""
Set is_cancelled=1 in all original gl entries for the voucher
"""
- frappe.db.sql("""update `tabGL Entry` set is_cancelled = 1,
+ frappe.db.sql("""UPDATE `tabGL Entry` SET is_cancelled = 1,
modified=%s, modified_by=%s
where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
(now(), frappe.session.user, voucher_type, voucher_no))
diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
index a76ffee..518d665 100644
--- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
+++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
@@ -12,7 +12,22 @@
"reqd": 1
},
{
- reqd: 1,
+ "fieldname":"from_date",
+ "label": __("From Date"),
+ "fieldtype": "Date",
+ "width": "80",
+ "reqd": 1,
+ "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
+ },
+ {
+ "fieldname":"to_date",
+ "label": __("To Date"),
+ "fieldtype": "Date",
+ "width": "80",
+ "reqd": 1,
+ "default": frappe.datetime.get_today()
+ },
+ {
default: "",
options: "Item",
label: __("Item"),
@@ -45,13 +60,12 @@
}
},
{
- fieldtype: "Link",
+ fieldtype: "MultiSelectList",
label: __("Supplier Quotation"),
- options: "Supplier Quotation",
fieldname: "supplier_quotation",
default: "",
- get_query: () => {
- return { filters: { "docstatus": ["<", 2] } }
+ get_data: function(txt) {
+ return frappe.db.get_link_options('Supplier Quotation', txt, {'docstatus': ["<", 2]});
}
},
{
@@ -63,9 +77,30 @@
get_query: () => {
return { filters: { "docstatus": ["<", 2] } }
}
+ },
+ {
+ fieldtype: "Check",
+ label: __("Include Expired"),
+ fieldname: "include_expired",
+ default: 0
}
],
+ formatter: (value, row, column, data, default_formatter) => {
+ value = default_formatter(value, row, column, data);
+
+ if(column.fieldname === "valid_till" && data.valid_till){
+ if(frappe.datetime.get_diff(data.valid_till, frappe.datetime.nowdate()) <= 1){
+ value = `<div style="color:red">${value}</div>`;
+ }
+ else if (frappe.datetime.get_diff(data.valid_till, frappe.datetime.nowdate()) <= 7){
+ value = `<div style="color:darkorange">${value}</div>`;
+ }
+ }
+
+ return value;
+ },
+
onload: (report) => {
// Create a button for setting the default supplier
report.page.add_inner_button(__("Select Default Supplier"), () => {
diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py
index a33867a..4426560 100644
--- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py
+++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py
@@ -16,44 +16,49 @@
supplier_quotation_data = get_data(filters, conditions)
columns = get_columns()
- data, chart_data = prepare_data(supplier_quotation_data)
+ data, chart_data = prepare_data(supplier_quotation_data, filters)
+ message = get_message()
- return columns, data, None, chart_data
+ return columns, data, message, chart_data
def get_conditions(filters):
conditions = ""
+ if filters.get("item_code"):
+ conditions += " AND sqi.item_code = %(item_code)s"
+
if filters.get("supplier_quotation"):
- conditions += " AND sqi.parent = %(supplier_quotation)s"
+ conditions += " AND sqi.parent in %(supplier_quotation)s"
if filters.get("request_for_quotation"):
conditions += " AND sqi.request_for_quotation = %(request_for_quotation)s"
if filters.get("supplier"):
conditions += " AND sq.supplier in %(supplier)s"
+
+ if not filters.get("include_expired"):
+ conditions += " AND sq.status != 'Expired'"
+
return conditions
def get_data(filters, conditions):
- if not filters.get("item_code"):
- return []
-
supplier_quotation_data = frappe.db.sql("""SELECT
- sqi.parent, sqi.qty, sqi.rate, sqi.uom, sqi.request_for_quotation,
- sq.supplier
+ sqi.parent, sqi.item_code, sqi.qty, sqi.rate, sqi.uom, sqi.request_for_quotation,
+ sqi.lead_time_days, sq.supplier, sq.valid_till
FROM
`tabSupplier Quotation Item` sqi,
`tabSupplier Quotation` sq
WHERE
- sqi.item_code = %(item_code)s
- AND sqi.parent = sq.name
+ sqi.parent = sq.name
AND sqi.docstatus < 2
AND sq.company = %(company)s
- AND sq.status != 'Expired'
- {0}""".format(conditions), filters, as_dict=1)
+ AND sq.transaction_date between %(from_date)s and %(to_date)s
+ {0}
+ order by sq.transaction_date, sqi.item_code""".format(conditions), filters, as_dict=1)
return supplier_quotation_data
-def prepare_data(supplier_quotation_data):
- out, suppliers, qty_list = [], [], []
+def prepare_data(supplier_quotation_data, filters):
+ out, suppliers, qty_list, chart_data = [], [], [], []
supplier_wise_map = defaultdict(list)
supplier_qty_price_map = {}
@@ -70,20 +75,24 @@
exchange_rate = 1
row = {
+ "item_code": data.get('item_code'),
"quotation": data.get("parent"),
"qty": data.get("qty"),
"price": flt(data.get("rate") * exchange_rate, float_precision),
"uom": data.get("uom"),
"request_for_quotation": data.get("request_for_quotation"),
+ "valid_till": data.get('valid_till'),
+ "lead_time_days": data.get('lead_time_days')
}
# map for report view of form {'supplier1':[{},{},...]}
supplier_wise_map[supplier].append(row)
# map for chart preparation of the form {'supplier1': {'qty': 'price'}}
- if not supplier in supplier_qty_price_map:
- supplier_qty_price_map[supplier] = {}
- supplier_qty_price_map[supplier][row["qty"]] = row["price"]
+ if filters.get("item_code"):
+ if not supplier in supplier_qty_price_map:
+ supplier_qty_price_map[supplier] = {}
+ supplier_qty_price_map[supplier][row["qty"]] = row["price"]
suppliers.append(supplier)
qty_list.append(data.get("qty"))
@@ -97,7 +106,8 @@
for entry in supplier_wise_map[supplier]:
out.append(entry)
- chart_data = prepare_chart_data(suppliers, qty_list, supplier_qty_price_map)
+ if filters.get("item_code"):
+ chart_data = prepare_chart_data(suppliers, qty_list, supplier_qty_price_map)
return out, chart_data
@@ -117,9 +127,10 @@
data_points_map[qty].append(None)
dataset = []
+ currency_symbol = frappe.db.get_value("Currency", frappe.db.get_default("currency"), "symbol")
for qty in qty_list:
datapoints = {
- "name": _("Price for Qty ") + str(qty),
+ "name": currency_symbol + " (Qty " + str(qty) + " )",
"values": data_points_map[qty]
}
dataset.append(datapoints)
@@ -140,14 +151,21 @@
"label": _("Supplier"),
"fieldtype": "Link",
"options": "Supplier",
+ "width": 150
+ },
+ {
+ "fieldname": "item_code",
+ "label": _("Item"),
+ "fieldtype": "Link",
+ "options": "Item",
"width": 200
},
{
- "fieldname": "quotation",
- "label": _("Supplier Quotation"),
+ "fieldname": "uom",
+ "label": _("UOM"),
"fieldtype": "Link",
- "options": "Supplier Quotation",
- "width": 200
+ "options": "UOM",
+ "width": 90
},
{
"fieldname": "qty",
@@ -163,19 +181,43 @@
"width": 110
},
{
- "fieldname": "uom",
- "label": _("UOM"),
+ "fieldname": "quotation",
+ "label": _("Supplier Quotation"),
"fieldtype": "Link",
- "options": "UOM",
- "width": 90
+ "options": "Supplier Quotation",
+ "width": 200
+ },
+ {
+ "fieldname": "valid_till",
+ "label": _("Valid Till"),
+ "fieldtype": "Date",
+ "width": 100
+ },
+ {
+ "fieldname": "lead_time_days",
+ "label": _("Lead Time (Days)"),
+ "fieldtype": "Int",
+ "width": 100
},
{
"fieldname": "request_for_quotation",
"label": _("Request for Quotation"),
"fieldtype": "Link",
"options": "Request for Quotation",
- "width": 200
+ "width": 150
}
]
- return columns
\ No newline at end of file
+ return columns
+
+def get_message():
+ return """<span class="indicator">
+ Valid till :
+ </span>
+ <span class="indicator orange">
+ Expires in a week or less
+ </span>
+
+ <span class="indicator red">
+ Expires today / Already Expired
+ </span>"""
\ No newline at end of file
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index b75f7bd..2f6cd25 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -18,6 +18,7 @@
from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty
from erpnext.loan_management.doctype.loan_application.loan_application import create_pledge
from erpnext.loan_management.doctype.loan_disbursement.loan_disbursement import get_disbursal_amount
+from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts
class TestLoan(unittest.TestCase):
def setUp(self):
@@ -194,18 +195,14 @@
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
- repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5),
+ repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 6),
"Loan Closure", flt(loan.loan_amount + accrued_interest_amount))
repayment_entry.submit()
amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
'paid_principal_amount'])
- unaccrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * 6) \
- / (days_in_year(get_datetime(first_date).year) * 100)
-
- self.assertEquals(flt(amounts[0] + unaccrued_interest_amount, 3),
- flt(accrued_interest_amount, 3))
+ self.assertEquals(flt(amounts[0], 2),flt(accrued_interest_amount, 2))
self.assertEquals(flt(repayment_entry.penalty_amount, 5), 0)
loan.load_from_db()
@@ -307,9 +304,6 @@
"Loan Closure", flt(loan.loan_amount + accrued_interest_amount))
repayment_entry.submit()
- amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
- 'paid_principal_amount'])
-
loan.load_from_db()
self.assertEquals(loan.status, "Loan Closure Requested")
@@ -374,6 +368,47 @@
self.assertEqual(get_disbursal_amount(loan.name), 300000)
+ def test_pending_loan_amount_after_closure_request(self):
+ pledge = [{
+ "loan_security": "Test Security 1",
+ "qty": 4000.00
+ }]
+
+ loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+ create_pledge(loan_application)
+
+ loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date=get_first_day(nowdate()))
+ loan.submit()
+
+ self.assertEquals(loan.loan_amount, 1000000)
+
+ first_date = '2019-10-01'
+ last_date = '2019-10-30'
+
+ no_of_days = date_diff(last_date, first_date) + 1
+
+ no_of_days += 6
+
+ accrued_interest_amount = (loan.loan_amount * loan.rate_of_interest * no_of_days) \
+ / (days_in_year(get_datetime(first_date).year) * 100)
+
+ make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
+ process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+
+ amounts = calculate_amounts(loan.name, add_days(last_date, 6), "Regular Repayment")
+
+ repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 6),
+ "Loan Closure", flt(loan.loan_amount + accrued_interest_amount))
+ repayment_entry.submit()
+
+ amounts = frappe.db.get_value('Loan Interest Accrual', {'loan': loan.name}, ['paid_interest_amount',
+ 'paid_principal_amount'])
+
+ loan.load_from_db()
+ self.assertEquals(loan.status, "Loan Closure Requested")
+
+ amounts = calculate_amounts(loan.name, add_days(last_date, 6), "Regular Repayment")
+ self.assertEquals(amounts['pending_principal_amount'], 0.0)
def create_loan_accounts():
if not frappe.db.exists("Account", "Loans and Advances (Assets) - _TC"):
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 451ae85..7d83e32 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -116,6 +116,7 @@
def allocate_amounts(self, paid_entries):
self.set('repayment_details', [])
self.principal_amount_paid = 0
+ total_interest_paid = 0
interest_paid = self.amount_paid - self.penalty_amount
if self.amount_paid - self.penalty_amount > 0 and paid_entries:
@@ -137,12 +138,19 @@
interest_paid = 0
paid_principal=0
+ total_interest_paid += interest_amount
self.append('repayment_details', {
'loan_interest_accrual': lia,
'paid_interest_amount': interest_amount,
'paid_principal_amount': paid_principal
})
+ if self.payment_type == 'Loan Closure' and total_interest_paid < self.interest_payable:
+ unaccrued_interest = self.interest_payable - total_interest_paid
+ interest_paid -= unaccrued_interest
+ if self.repayment_details:
+ self.repayment_details[-1].paid_interest_amount += unaccrued_interest
+
if interest_paid:
self.principal_amount_paid += interest_paid
@@ -297,7 +305,7 @@
if not final_due_date:
final_due_date = add_days(due_date, loan_type_details.grace_period_in_days)
- if against_loan_doc.status == 'Disbursed':
+ if against_loan_doc.status in ('Disbursed', 'Loan Closure Requested'):
pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid - against_loan_doc.total_interest_payable
else:
pending_principal_amount = against_loan_doc.disbursed_amount
diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
index 5e9d82a..f6b28da 100644
--- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
+++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
@@ -43,8 +43,10 @@
"valid_upto": (">=", get_datetime())
}, as_list=1))
- loan_amount, principal_paid = frappe.get_value("Loan", self.loan, ['loan_amount', 'total_principal_paid'])
- pending_principal_amount = loan_amount - principal_paid
+ total_payment, principal_paid, interest_payable = frappe.get_value("Loan", self.loan, ['total_payment', 'total_principal_paid',
+ 'total_interest_payable'])
+
+ pending_principal_amount = flt(total_payment) - flt(interest_payable) - flt(principal_paid)
security_value = 0
for security in self.securities:
@@ -60,7 +62,7 @@
security_value += qty_after_unpledge * loan_security_price_map.get(security.loan_security)
- if not security_value and pending_principal_amount > 0:
+ if not security_value and flt(pending_principal_amount, 2) > 0:
frappe.throw("Cannot Unpledge, loan to value ratio is breaching")
if security_value and (pending_principal_amount/security_value) * 100 > ltv_ratio:
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 436a232..792235f 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -781,10 +781,23 @@
else var date = this.frm.doc.transaction_date;
if (frappe.meta.get_docfield(this.frm.doctype, "shipping_address") &&
- in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)){
+ in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)) {
erpnext.utils.get_shipping_address(this.frm, function(){
set_party_account(set_pricing);
})
+
+ // Get default company billing address in Purchase Invoice, Order and Receipt
+ frappe.call({
+ 'method': 'frappe.contacts.doctype.address.address.get_default_address',
+ 'args': {
+ 'doctype': 'Company',
+ 'name': this.frm.doc.company
+ },
+ 'callback': function(r) {
+ me.frm.set_value('billing_address', r.message);
+ }
+ });
+
} else {
set_party_account(set_pricing);
}
diff --git a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.js b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.js
index dfdf9dc..b757d53 100644
--- a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.js
+++ b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.js
@@ -46,5 +46,28 @@
],
onload: (report) => {
fetch_gstins(report);
+
+ report.page.add_inner_button(__("Download JSON"), function () {
+ var filters = report.get_values();
+
+ frappe.call({
+ method: 'erpnext.regional.report.hsn_wise_summary_of_outward_supplies.hsn_wise_summary_of_outward_supplies.get_json',
+ args: {
+ data: report.data,
+ report_name: report.report_name,
+ filters: filters
+ },
+ callback: function(r) {
+ if (r.message) {
+ const args = {
+ cmd: 'erpnext.regional.report.hsn_wise_summary_of_outward_supplies.hsn_wise_summary_of_outward_supplies.download_json_file',
+ data: r.message.data,
+ report_name: r.message.report_name
+ };
+ open_url_post(frappe.request.url, args);
+ }
+ }
+ });
+ });
}
};
diff --git a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py
index a3ed4ce..59389ce 100644
--- a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py
+++ b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py
@@ -4,11 +4,13 @@
from __future__ import unicode_literals
import frappe, erpnext
from frappe import _
-from frappe.utils import flt
+from frappe.utils import flt, getdate, cstr
from frappe.model.meta import get_field_precision
from frappe.utils.xlsxutils import handle_html
from six import iteritems
import json
+from erpnext.regional.india.utils import get_gst_accounts
+from erpnext.regional.report.gstr_1.gstr_1 import get_company_gstin_number
def execute(filters=None):
return _execute(filters)
@@ -141,7 +143,7 @@
tax_details = frappe.db.sql("""
select
- parent, description, item_wise_tax_detail,
+ parent, account_head, item_wise_tax_detail,
base_tax_amount_after_discount_amount
from `tab%s`
where
@@ -153,11 +155,11 @@
""" % (tax_doctype, '%s', ', '.join(['%s']*len(invoice_item_row)), conditions),
tuple([doctype] + list(invoice_item_row)))
- for parent, description, item_wise_tax_detail, tax_amount in tax_details:
- description = handle_html(description)
- if description not in tax_columns and tax_amount:
+ for parent, account_head, item_wise_tax_detail, tax_amount in tax_details:
+
+ if account_head not in tax_columns and tax_amount:
# as description is text editor earlier and markup can break the column convention in reports
- tax_columns.append(description)
+ tax_columns.append(account_head)
if item_wise_tax_detail:
try:
@@ -175,17 +177,17 @@
for d in item_row_map.get(parent, {}).get(item_code, []):
item_tax_amount = tax_amount
if item_tax_amount:
- itemised_tax.setdefault((parent, item_code), {})[description] = frappe._dict({
+ itemised_tax.setdefault((parent, item_code), {})[account_head] = frappe._dict({
"tax_amount": flt(item_tax_amount, tax_amount_precision)
})
except ValueError:
continue
tax_columns.sort()
- for desc in tax_columns:
+ for account_head in tax_columns:
columns.append({
- "label": desc,
- "fieldname": frappe.scrub(desc),
+ "label": account_head,
+ "fieldname": frappe.scrub(account_head),
"fieldtype": "Float",
"width": 110
})
@@ -212,3 +214,76 @@
return result
+@frappe.whitelist()
+def get_json(filters, report_name, data):
+ filters = json.loads(filters)
+ report_data = json.loads(data)
+ gstin = filters.get('company_gstin') or get_company_gstin_number(filters["company"])
+
+ if not filters.get('from_date') or not filters.get('to_date'):
+ frappe.throw(_("Please enter From Date and To Date to generate JSON"))
+
+ fp = "%02d%s" % (getdate(filters["to_date"]).month, getdate(filters["to_date"]).year)
+
+ gst_json = {"version": "GST2.3.4",
+ "hash": "hash", "gstin": gstin, "fp": fp}
+
+ gst_json["hsn"] = {
+ "data": get_hsn_wise_json_data(filters, report_data)
+ }
+
+ return {
+ 'report_name': report_name,
+ 'data': gst_json
+ }
+
+@frappe.whitelist()
+def download_json_file():
+ '''download json content in a file'''
+ data = frappe._dict(frappe.local.form_dict)
+ frappe.response['filename'] = frappe.scrub("{0}".format(data['report_name'])) + '.json'
+ frappe.response['filecontent'] = data['data']
+ frappe.response['content_type'] = 'application/json'
+ frappe.response['type'] = 'download'
+
+def get_hsn_wise_json_data(filters, report_data):
+
+ filters = frappe._dict(filters)
+ gst_accounts = get_gst_accounts(filters.company)
+ data = []
+ count = 1
+
+ for hsn in report_data:
+ row = {
+ "num": count,
+ "hsn_sc": hsn.get("gst_hsn_code"),
+ "desc": hsn.get("description"),
+ "uqc": hsn.get("stock_uom").upper(),
+ "qty": hsn.get("stock_qty"),
+ "val": flt(hsn.get("total_amount"), 2),
+ "txval": flt(hsn.get("taxable_amount", 2)),
+ "iamt": 0.0,
+ "camt": 0.0,
+ "samt": 0.0,
+ "csamt": 0.0
+
+ }
+
+ for account in gst_accounts.get('igst_account'):
+ row['iamt'] += flt(hsn.get(frappe.scrub(cstr(account)), 0.0), 2)
+
+ for account in gst_accounts.get('cgst_account'):
+ row['camt'] += flt(hsn.get(frappe.scrub(cstr(account)), 0.0), 2)
+
+ for account in gst_accounts.get('sgst_account'):
+ row['samt'] += flt(hsn.get(frappe.scrub(cstr(account)), 0.0), 2)
+
+ for account in gst_accounts.get('cess_account'):
+ row['csamt'] += flt(hsn.get(frappe.scrub(cstr(account)), 0.0), 2)
+
+ data.append(row)
+ count +=1
+
+ return data
+
+