Merge branch 'develop' of https://github.com/frappe/erpnext into gstr-3b-pos-fixes

This commit is contained in:
deepeshgarg007 2019-06-14 13:14:51 +05:30
commit c62518c094
13 changed files with 729 additions and 2761 deletions

View File

@ -5,7 +5,7 @@
<p>ERP made simple</p> <p>ERP made simple</p>
</p> </p>
[![Build Status](https://travis-ci.com/frappe/erpnext.png)](https://travis-ci.com/frappe/erpnext) [![Build Status](https://travis-ci.com/frappe/erpnext.svg)](https://travis-ci.com/frappe/erpnext)
[![Open Source Helpers](https://www.codetriage.com/frappe/erpnext/badges/users.svg)](https://www.codetriage.com/frappe/erpnext) [![Open Source Helpers](https://www.codetriage.com/frappe/erpnext/badges/users.svg)](https://www.codetriage.com/frappe/erpnext)
[![Coverage Status](https://coveralls.io/repos/github/frappe/erpnext/badge.svg?branch=develop)](https://coveralls.io/github/frappe/erpnext?branch=develop) [![Coverage Status](https://coveralls.io/repos/github/frappe/erpnext/badge.svg?branch=develop)](https://coveralls.io/github/frappe/erpnext?branch=develop)

View File

@ -60,7 +60,7 @@ def validate_filters(filters):
frappe.throw(_("Filter based on Cost Center is only applicable if Budget Against is selected as Cost Center")) frappe.throw(_("Filter based on Cost Center is only applicable if Budget Against is selected as Cost Center"))
def get_columns(filters): def get_columns(filters):
columns = [_(filters.get("budget_against")) + ":Link/%s:80"%(filters.get("budget_against")), _("Account") + ":Link/Account:80"] columns = [_(filters.get("budget_against")) + ":Link/%s:150"%(filters.get("budget_against")), _("Account") + ":Link/Account:150"]
group_months = False if filters["period"] == "Monthly" else True group_months = False if filters["period"] == "Monthly" else True
@ -71,7 +71,7 @@ def get_columns(filters):
if filters["period"] == "Yearly": if filters["period"] == "Yearly":
labels = [_("Budget") + " " + str(year[0]), _("Actual ") + " " + str(year[0]), _("Varaiance ") + " " + str(year[0])] labels = [_("Budget") + " " + str(year[0]), _("Actual ") + " " + str(year[0]), _("Varaiance ") + " " + str(year[0])]
for label in labels: for label in labels:
columns.append(label+":Float:80") columns.append(label+":Float:150")
else: else:
for label in [_("Budget") + " (%s)" + " " + str(year[0]), _("Actual") + " (%s)" + " " + str(year[0]), _("Variance") + " (%s)" + " " + str(year[0])]: for label in [_("Budget") + " (%s)" + " " + str(year[0]), _("Actual") + " (%s)" + " " + str(year[0]), _("Variance") + " (%s)" + " " + str(year[0])]:
if group_months: if group_months:
@ -79,11 +79,11 @@ def get_columns(filters):
else: else:
label = label % formatdate(from_date, format_string="MMM") label = label % formatdate(from_date, format_string="MMM")
columns.append(label+":Float:80") columns.append(label+":Float:150")
if filters["period"] != "Yearly" : if filters["period"] != "Yearly" :
return columns + [_("Total Budget") + ":Float:80", _("Total Actual") + ":Float:80", return columns + [_("Total Budget") + ":Float:150", _("Total Actual") + ":Float:150",
_("Total Variance") + ":Float:80"] _("Total Variance") + ":Float:150"]
else: else:
return columns return columns

View File

@ -180,19 +180,27 @@ def calculate_values(accounts, gl_entries_by_account, opening_balances, filters,
if d["root_type"] == "Asset" or d["root_type"] == "Equity" or d["root_type"] == "Expense": if d["root_type"] == "Asset" or d["root_type"] == "Equity" or d["root_type"] == "Expense":
d["opening_debit"] -= d["opening_credit"] d["opening_debit"] -= d["opening_credit"]
d["opening_credit"] = 0.0 d["closing_debit"] -= d["closing_credit"]
total_row["opening_debit"] += d["opening_debit"]
# For opening
check_opening_closing_has_negative_value(d, "opening_debit", "opening_credit")
# For closing
check_opening_closing_has_negative_value(d, "closing_debit", "closing_credit")
if d["root_type"] == "Liability" or d["root_type"] == "Income": if d["root_type"] == "Liability" or d["root_type"] == "Income":
d["opening_credit"] -= d["opening_debit"] d["opening_credit"] -= d["opening_debit"]
d["opening_debit"] = 0.0
total_row["opening_credit"] += d["opening_credit"]
if d["root_type"] == "Asset" or d["root_type"] == "Equity" or d["root_type"] == "Expense":
d["closing_debit"] -= d["closing_credit"]
d["closing_credit"] = 0.0
total_row["closing_debit"] += d["closing_debit"]
if d["root_type"] == "Liability" or d["root_type"] == "Income":
d["closing_credit"] -= d["closing_debit"] d["closing_credit"] -= d["closing_debit"]
d["closing_debit"] = 0.0
# For opening
check_opening_closing_has_negative_value(d, "opening_credit", "opening_debit")
# For closing
check_opening_closing_has_negative_value(d, "closing_credit", "closing_debit")
total_row["opening_debit"] += d["opening_debit"]
total_row["closing_debit"] += d["closing_debit"]
total_row["opening_credit"] += d["opening_credit"]
total_row["closing_credit"] += d["closing_credit"] total_row["closing_credit"] += d["closing_credit"]
return total_row return total_row
@ -219,8 +227,6 @@ def prepare_data(accounts, filters, total_row, parent_children_map, company_curr
if d.account_number else d.account_name) if d.account_number else d.account_name)
} }
prepare_opening_and_closing(d)
for key in value_fields: for key in value_fields:
row[key] = flt(d.get(key, 0.0), 3) row[key] = flt(d.get(key, 0.0), 3)
@ -295,22 +301,11 @@ def get_columns():
} }
] ]
def prepare_opening_and_closing(d): def check_opening_closing_has_negative_value(d, dr_or_cr, switch_to_column):
d["closing_debit"] = d["opening_debit"] + d["debit"] # If opening debit has negetive value then move it to opening credit and vice versa.
d["closing_credit"] = d["opening_credit"] + d["credit"]
if d["root_type"] == "Asset" or d["root_type"] == "Equity" or d["root_type"] == "Expense": if d[dr_or_cr] < 0:
d["opening_debit"] -= d["opening_credit"] d[switch_to_column] = abs(d[dr_or_cr])
d["opening_credit"] = 0.0 d[dr_or_cr] = 0.0
else:
if d["root_type"] == "Liability" or d["root_type"] == "Income": d[switch_to_column] = 0.0
d["opening_credit"] -= d["opening_debit"]
d["opening_debit"] = 0.0
if d["root_type"] == "Asset" or d["root_type"] == "Equity" or d["root_type"] == "Expense":
d["closing_debit"] -= d["closing_credit"]
d["closing_credit"] = 0.0
if d["root_type"] == "Liability" or d["root_type"] == "Income":
d["closing_credit"] -= d["closing_debit"]
d["closing_debit"] = 0.0

View File

@ -448,6 +448,8 @@ class SalarySlip(TransactionBase):
if not overwrite and component_row.default_amount: if not overwrite and component_row.default_amount:
amount += component_row.default_amount amount += component_row.default_amount
else:
component_row.default_amount = amount
component_row.amount = amount component_row.amount = amount
component_row.deduct_full_tax_on_selected_payroll_date = struct_row.deduct_full_tax_on_selected_payroll_date component_row.deduct_full_tax_on_selected_payroll_date = struct_row.deduct_full_tax_on_selected_payroll_date

View File

@ -268,18 +268,19 @@ erpnext.SerialNoBatchSelector = Class.extend({
{fieldname: 'batches', fieldtype: 'Table', label: __('Batch Entries'), {fieldname: 'batches', fieldtype: 'Table', label: __('Batch Entries'),
fields: [ fields: [
{ {
fieldtype:'Link', 'fieldtype': 'Link',
fieldname:'batch_no', 'read_only': 0,
options: 'Batch', 'fieldname': 'batch_no',
label: __('Select Batch'), 'options': 'Batch',
in_list_view:1, 'label': __('Select Batch'),
'in_list_view': 1,
get_query: function () { get_query: function () {
return { return {
filters: { item: me.item_code }, filters: { item: me.item_code },
query: 'erpnext.controllers.queries.get_batch_numbers' query: 'erpnext.controllers.queries.get_batch_numbers'
}; };
}, },
onchange: function(e) { change: function () {
let val = this.get_value(); let val = this.get_value();
if (val.length === 0) { if (val.length === 0) {
this.grid_row.on_grid_fields_dict this.grid_row.on_grid_fields_dict
@ -323,23 +324,24 @@ erpnext.SerialNoBatchSelector = Class.extend({
} }
}, },
{ {
fieldtype:'Float', 'fieldtype': 'Float',
read_only:1, 'read_only': 1,
fieldname:'available_qty', 'fieldname': 'available_qty',
label: __('Available'), 'label': __('Available'),
in_list_view:1, 'in_list_view': 1,
default: 0, 'default': 0,
onchange: function() { change: function () {
this.grid_row.on_grid_fields_dict.selected_qty.set_value('0'); this.grid_row.on_grid_fields_dict.selected_qty.set_value('0');
} }
}, },
{ {
fieldtype:'Float', 'fieldtype': 'Float',
fieldname:'selected_qty', 'read_only': 0,
label: __('Qty'), 'fieldname': 'selected_qty',
in_list_view:1, 'label': __('Qty'),
'in_list_view': 1,
'default': 0, 'default': 0,
onchange: function(e) { change: function () {
var batch_no = this.grid_row.on_grid_fields_dict.batch_no.get_value(); var batch_no = this.grid_row.on_grid_fields_dict.batch_no.get_value();
var available_qty = this.grid_row.on_grid_fields_dict.available_qty.get_value(); var available_qty = this.grid_row.on_grid_fields_dict.available_qty.get_value();
var selected_qty = this.grid_row.on_grid_fields_dict.selected_qty.get_value(); var selected_qty = this.grid_row.on_grid_fields_dict.selected_qty.get_value();

View File

@ -14,6 +14,15 @@ def validate_gstin_for_india(doc, method):
if not hasattr(doc, 'gstin') or not doc.gstin: if not hasattr(doc, 'gstin') or not doc.gstin:
return return
gst_category = []
if len(doc.links):
link_doctype = doc.links[0].get("link_doctype")
link_name = doc.links[0].get("link_name")
if link_doctype in ["Customer", "Supplier"]:
gst_category = frappe.db.get_value(link_doctype, {'name': link_name}, ['gst_category'])
doc.gstin = doc.gstin.upper().strip() doc.gstin = doc.gstin.upper().strip()
if not doc.gstin or doc.gstin == 'NA': if not doc.gstin or doc.gstin == 'NA':
return return
@ -21,6 +30,11 @@ def validate_gstin_for_india(doc, method):
if len(doc.gstin) != 15: if len(doc.gstin) != 15:
frappe.throw(_("Invalid GSTIN! A GSTIN must have 15 characters.")) frappe.throw(_("Invalid GSTIN! A GSTIN must have 15 characters."))
if gst_category and gst_category == 'UIN Holders':
p = re.compile("^[0-9]{4}[A-Z]{3}[0-9]{5}[0-9A-Z]{3}")
if not p.match(doc.gstin):
frappe.throw(_("Invalid GSTIN! The input you've entered doesn't match the GSTIN format for UIN Holders or Non-Resident OIDAR Service Providers"))
else:
p = re.compile("^[0-9]{2}[A-Z]{4}[0-9A-Z]{1}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[1-9A-Z]{1}[0-9A-Z]{1}$") p = re.compile("^[0-9]{2}[A-Z]{4}[0-9A-Z]{1}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[1-9A-Z]{1}[0-9A-Z]{1}$")
if not p.match(doc.gstin): if not p.match(doc.gstin):
frappe.throw(_("Invalid GSTIN! The input you've entered doesn't match the format of GSTIN.")) frappe.throw(_("Invalid GSTIN! The input you've entered doesn't match the format of GSTIN."))

View File

@ -0,0 +1,32 @@
frappe.query_reports["DATEV"] = {
"filters": [
{
"fieldname": "company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
"default": frappe.defaults.get_user_default("Company") || frappe.defaults.get_global_default("Company"),
"reqd": 1
},
{
"fieldname": "from_date",
"label": __("From Date"),
"default": frappe.datetime.month_start(),
"fieldtype": "Date",
"reqd": 1
},
{
"fieldname": "to_date",
"label": __("To Date"),
"default": frappe.datetime.now_date(),
"fieldtype": "Date",
"reqd": 1
}
],
onload: function(query_report) {
query_report.page.add_inner_button("Download DATEV Export", () => {
const filters = JSON.stringify(query_report.get_values());
window.open(`/api/method/erpnext.regional.report.datev.datev.download_datev_csv?filters=${filters}`);
});
}
};

View File

@ -0,0 +1,29 @@
{
"add_total_row": 0,
"apply_user_permissions": 0,
"creation": "2019-04-24 08:45:16.650129",
"disabled": 0,
"icon": "octicon octicon-repo-pull",
"color": "#4CB944",
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"module": "Regional",
"name": "DATEV",
"owner": "Administrator",
"ref_doctype": "GL Entry",
"report_name": "DATEV",
"report_type": "Script Report",
"roles": [
{
"role": "Accounts User"
},
{
"role": "Accounts Manager"
},
{
"role": "Auditor"
}
]
}

View File

@ -0,0 +1,373 @@
# coding: utf-8
"""
Provide a report and downloadable CSV according to the German DATEV format.
- Query report showing only the columns that contain data, formatted nicely for
dispay to the user.
- CSV download functionality `download_datev_csv` that provides a CSV file with
all required columns. Used to import the data into the DATEV Software.
"""
from __future__ import unicode_literals
import json
from six import string_types
import frappe
from frappe import _
import pandas as pd
def execute(filters=None):
"""Entry point for frappe."""
validate_filters(filters)
result = get_gl_entries(filters, as_dict=0)
columns = get_columns()
return columns, result
def validate_filters(filters):
"""Make sure all mandatory filters are present."""
if not filters.get('company'):
frappe.throw(_('{0} is mandatory').format(_('Company')))
if not filters.get('from_date'):
frappe.throw(_('{0} is mandatory').format(_('From Date')))
if not filters.get('to_date'):
frappe.throw(_('{0} is mandatory').format(_('To Date')))
def get_columns():
"""Return the list of columns that will be shown in query report."""
columns = [
{
"label": "Umsatz (ohne Soll/Haben-Kz)",
"fieldname": "Umsatz (ohne Soll/Haben-Kz)",
"fieldtype": "Currency",
},
{
"label": "Soll/Haben-Kennzeichen",
"fieldname": "Soll/Haben-Kennzeichen",
"fieldtype": "Data",
},
{
"label": "Kontonummer",
"fieldname": "Kontonummer",
"fieldtype": "Data",
},
{
"label": "Gegenkonto (ohne BU-Schlüssel)",
"fieldname": "Gegenkonto (ohne BU-Schlüssel)",
"fieldtype": "Data",
},
{
"label": "Belegdatum",
"fieldname": "Belegdatum",
"fieldtype": "Date",
},
{
"label": "Buchungstext",
"fieldname": "Buchungstext",
"fieldtype": "Text",
},
{
"label": "Beleginfo - Art 1",
"fieldname": "Beleginfo - Art 1",
"fieldtype": "Data",
},
{
"label": "Beleginfo - Inhalt 1",
"fieldname": "Beleginfo - Inhalt 1",
"fieldtype": "Data",
},
{
"label": "Beleginfo - Art 2",
"fieldname": "Beleginfo - Art 2",
"fieldtype": "Data",
},
{
"label": "Beleginfo - Inhalt 2",
"fieldname": "Beleginfo - Inhalt 2",
"fieldtype": "Data",
}
]
return columns
def get_gl_entries(filters, as_dict):
"""
Get a list of accounting entries.
Select GL Entries joined with Account and Party Account in order to get the
account numbers. Returns a list of accounting entries.
Arguments:
filters -- dict of filters to be passed to the sql query
as_dict -- return as list of dicts [0,1]
"""
gl_entries = frappe.db.sql("""
select
/* either debit or credit amount; always positive */
case gl.debit when 0 then gl.credit else gl.debit end as 'Umsatz (ohne Soll/Haben-Kz)',
/* 'H' when credit, 'S' when debit */
case gl.debit when 0 then 'H' else 'S' end as 'Soll/Haben-Kennzeichen',
/* account number or, if empty, party account number */
coalesce(acc.account_number, acc_pa.account_number) as 'Kontonummer',
/* against number or, if empty, party against number */
coalesce(acc_against.account_number, acc_against_pa.account_number) as 'Gegenkonto (ohne BU-Schlüssel)',
gl.posting_date as 'Belegdatum',
gl.remarks as 'Buchungstext',
gl.voucher_type as 'Beleginfo - Art 1',
gl.voucher_no as 'Beleginfo - Inhalt 1',
gl.against_voucher_type as 'Beleginfo - Art 2',
gl.against_voucher as 'Beleginfo - Inhalt 2'
from `tabGL Entry` gl
/* Statistisches Konto (Debitoren/Kreditoren) */
left join `tabParty Account` pa
on gl.against = pa.parent
and gl.company = pa.company
/* Kontonummer */
left join `tabAccount` acc
on gl.account = acc.name
/* Gegenkonto-Nummer */
left join `tabAccount` acc_against
on gl.against = acc_against.name
/* Statistische Kontonummer */
left join `tabAccount` acc_pa
on pa.account = acc_pa.name
/* Statistische Gegenkonto-Nummer */
left join `tabAccount` acc_against_pa
on pa.account = acc_against_pa.name
where gl.company = %(company)s
and DATE(gl.posting_date) >= %(from_date)s
and DATE(gl.posting_date) <= %(to_date)s
order by 'Belegdatum', gl.voucher_no""", filters, as_dict=as_dict)
return gl_entries
def get_datev_csv(data):
"""
Fill in missing columns and return a CSV in DATEV Format.
Arguments:
data -- array of dictionaries
"""
columns = [
# All possible columns must tbe listed here, because DATEV requires them to
# be present in the CSV.
# ---
# Umsatz
"Umsatz (ohne Soll/Haben-Kz)",
"Soll/Haben-Kennzeichen",
"WKZ Umsatz",
"Kurs",
"Basis-Umsatz",
"WKZ Basis-Umsatz",
# Konto/Gegenkonto
"Kontonummer",
"Gegenkonto (ohne BU-Schlüssel)",
"BU-Schlüssel",
# Datum
"Belegdatum",
# Belegfelder
"Belegfeld 1",
"Belegfeld 2",
# Weitere Felder
"Skonto",
"Buchungstext",
# OPOS-Informationen
"Postensperre",
"Diverse Adressnummer",
"Geschäftspartnerbank",
"Sachverhalt",
"Zinssperre",
# Digitaler Beleg
"Beleglink",
# Beleginfo
"Beleginfo - Art 1",
"Beleginfo - Inhalt 1",
"Beleginfo - Art 2",
"Beleginfo - Inhalt 2",
"Beleginfo - Art 3",
"Beleginfo - Inhalt 3",
"Beleginfo - Art 4",
"Beleginfo - Inhalt 4",
"Beleginfo - Art 5",
"Beleginfo - Inhalt 5",
"Beleginfo - Art 6",
"Beleginfo - Inhalt 6",
"Beleginfo - Art 7",
"Beleginfo - Inhalt 7",
"Beleginfo - Art 8",
"Beleginfo - Inhalt 8",
# Kostenrechnung
"Kost 1 - Kostenstelle",
"Kost 2 - Kostenstelle",
"Kost-Menge",
# Steuerrechnung
"EU-Land u. UStID",
"EU-Steuersatz",
"Abw. Versteuerungsart",
# L+L Sachverhalt
"Sachverhalt L+L",
"Funktionsergänzung L+L",
# Funktion Steuerschlüssel 49
"BU 49 Hauptfunktionstyp",
"BU 49 Hauptfunktionsnummer",
"BU 49 Funktionsergänzung",
# Zusatzinformationen
"Zusatzinformation - Art 1",
"Zusatzinformation - Inhalt 1",
"Zusatzinformation - Art 2",
"Zusatzinformation - Inhalt 2",
"Zusatzinformation - Art 3",
"Zusatzinformation - Inhalt 3",
"Zusatzinformation - Art 4",
"Zusatzinformation - Inhalt 4",
"Zusatzinformation - Art 5",
"Zusatzinformation - Inhalt 5",
"Zusatzinformation - Art 6",
"Zusatzinformation - Inhalt 6",
"Zusatzinformation - Art 7",
"Zusatzinformation - Inhalt 7",
"Zusatzinformation - Art 8",
"Zusatzinformation - Inhalt 8",
"Zusatzinformation - Art 9",
"Zusatzinformation - Inhalt 9",
"Zusatzinformation - Art 10",
"Zusatzinformation - Inhalt 10",
"Zusatzinformation - Art 11",
"Zusatzinformation - Inhalt 11",
"Zusatzinformation - Art 12",
"Zusatzinformation - Inhalt 12",
"Zusatzinformation - Art 13",
"Zusatzinformation - Inhalt 13",
"Zusatzinformation - Art 14",
"Zusatzinformation - Inhalt 14",
"Zusatzinformation - Art 15",
"Zusatzinformation - Inhalt 15",
"Zusatzinformation - Art 16",
"Zusatzinformation - Inhalt 16",
"Zusatzinformation - Art 17",
"Zusatzinformation - Inhalt 17",
"Zusatzinformation - Art 18",
"Zusatzinformation - Inhalt 18",
"Zusatzinformation - Art 19",
"Zusatzinformation - Inhalt 19",
"Zusatzinformation - Art 20",
"Zusatzinformation - Inhalt 20",
# Mengenfelder LuF
"Stück",
"Gewicht",
# Forderungsart
"Zahlweise",
"Forderungsart",
"Veranlagungsjahr",
"Zugeordnete Fälligkeit",
# Weitere Felder
"Skontotyp",
# Anzahlungen
"Auftragsnummer",
"Buchungstyp",
"USt-Schlüssel (Anzahlungen)",
"EU-Land (Anzahlungen)",
"Sachverhalt L+L (Anzahlungen)",
"EU-Steuersatz (Anzahlungen)",
"Erlöskonto (Anzahlungen)",
# Stapelinformationen
"Herkunft-Kz",
# Technische Identifikation
"Buchungs GUID",
# Kostenrechnung
"Kost-Datum",
# OPOS-Informationen
"SEPA-Mandatsreferenz",
"Skontosperre",
# Gesellschafter und Sonderbilanzsachverhalt
"Gesellschaftername",
"Beteiligtennummer",
"Identifikationsnummer",
"Zeichnernummer",
# OPOS-Informationen
"Postensperre bis",
# Gesellschafter und Sonderbilanzsachverhalt
"Bezeichnung SoBil-Sachverhalt",
"Kennzeichen SoBil-Buchung",
# Stapelinformationen
"Festschreibung",
# Datum
"Leistungsdatum",
"Datum Zuord. Steuerperiode",
# OPOS-Informationen
"Fälligkeit",
# Konto/Gegenkonto
"Generalumkehr (GU)",
# Steuersatz für Steuerschlüssel
"Steuersatz",
"Land"
]
empty_df = pd.DataFrame(columns=columns)
data_df = pd.DataFrame.from_records(data)
result = empty_df.append(data_df)
result["Belegdatum"] = pd.to_datetime(result["Belegdatum"])
return result.to_csv(
sep=b';',
# European decimal seperator
decimal=',',
# Windows "ANSI" encoding
encoding='latin_1',
# format date as DDMM
date_format='%d%m',
# Windows line terminator
line_terminator=b'\r\n',
# Do not number rows
index=False,
# Use all columns defined above
columns=columns
)
@frappe.whitelist()
def download_datev_csv(filters=None):
"""
Provide accounting entries for download in DATEV format.
Validate the filters, get the data, produce the CSV file and provide it for
download. Can be called like this:
GET /api/method/erpnext.regional.report.datev.datev.download_datev_csv
Arguments / Params:
filters -- dict of filters to be passed to the sql query
"""
if isinstance(filters, string_types):
filters = json.loads(filters)
validate_filters(filters)
data = get_gl_entries(filters, as_dict=1)
filename = 'DATEV_Buchungsstapel_{}-{}_bis_{}'.format(
filters.get('company'),
filters.get('from_date'),
filters.get('to_date')
)
frappe.response['result'] = get_datev_csv(data)
frappe.response['doctype'] = filename
frappe.response['type'] = 'csv'

View File

@ -209,6 +209,9 @@ class Gstr1Report(object):
sum(i.get('base_net_amount', 0) for i in items sum(i.get('base_net_amount', 0) for i in items
if i.item_code == d.item_code and i.parent == d.parent)) if i.item_code == d.item_code and i.parent == d.parent))
item_tax_rate = {}
if d.item_tax_rate:
item_tax_rate = json.loads(d.item_tax_rate) item_tax_rate = json.loads(d.item_tax_rate)
if item_tax_rate: if item_tax_rate:

View File

@ -43,8 +43,11 @@ class Gstr2Report(Gstr1Report):
self.get_igst_invoices() self.get_igst_invoices()
for inv, items_based_on_rate in self.items_based_on_tax_rate.items(): for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
invoice_details = self.invoices.get(inv) invoice_details = self.invoices.get(inv)
for rate, items in items_based_on_rate.items(): for key, items in items_based_on_rate.items():
row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items) rate = key[0]
account = key[1]
row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, account, items)
tax_amount = taxable_value * rate / 100 tax_amount = taxable_value * rate / 100
if inv in self.igst_invoices: if inv in self.igst_invoices:
row += [tax_amount, 0, 0] row += [tax_amount, 0, 0]

File diff suppressed because it is too large Load Diff