Merge branch 'develop' of https://github.com/frappe/erpnext into gstr-3b-pos-fixes
This commit is contained in:
commit
c62518c094
@ -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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -180,20 +180,28 @@ 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
|
|
||||||
total_row["closing_credit"] += d["closing_credit"]
|
# 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"]
|
||||||
|
|
||||||
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
|
|
||||||
|
@ -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
|
||||||
|
@ -268,26 +268,27 @@ 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'),
|
||||||
get_query: function() {
|
'in_list_view': 1,
|
||||||
|
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
|
||||||
.available_qty.set_value(0);
|
.available_qty.set_value(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let selected_batches = this.grid.grid_rows.map((row) => {
|
let selected_batches = this.grid.grid_rows.map((row) => {
|
||||||
if(row === this.grid_row) {
|
if (row === this.grid_row) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,12 +296,12 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
|||||||
return row.on_grid_fields_dict.batch_no.get_value();
|
return row.on_grid_fields_dict.batch_no.get_value();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if(selected_batches.includes(val)) {
|
if (selected_batches.includes(val)) {
|
||||||
this.set_value("");
|
this.set_value("");
|
||||||
frappe.throw(__(`Batch ${val} already selected.`));
|
frappe.throw(__(`Batch ${val} already selected.`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(me.warehouse_details.name) {
|
if (me.warehouse_details.name) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: 'erpnext.stock.doctype.batch.batch.get_batch_qty',
|
method: 'erpnext.stock.doctype.batch.batch.get_batch_qty',
|
||||||
args: {
|
args: {
|
||||||
@ -323,31 +324,32 @@ 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();
|
||||||
|
|
||||||
if(batch_no.length === 0 && parseInt(selected_qty)!==0) {
|
if (batch_no.length === 0 && parseInt(selected_qty) !== 0) {
|
||||||
frappe.throw(__("Please select a batch"));
|
frappe.throw(__("Please select a batch"));
|
||||||
}
|
}
|
||||||
if(me.warehouse_details.type === 'Source Warehouse' &&
|
if (me.warehouse_details.type === 'Source Warehouse' &&
|
||||||
parseFloat(available_qty) < parseFloat(selected_qty)) {
|
parseFloat(available_qty) < parseFloat(selected_qty)) {
|
||||||
|
|
||||||
this.set_value('0');
|
this.set_value('0');
|
||||||
@ -363,7 +365,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
|||||||
],
|
],
|
||||||
in_place_edit: true,
|
in_place_edit: true,
|
||||||
data: this.data,
|
data: this.data,
|
||||||
get_data: function() {
|
get_data: function () {
|
||||||
return this.data;
|
return this.data;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -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,26 +30,31 @@ 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."))
|
||||||
|
|
||||||
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 gst_category and gst_category == 'UIN Holders':
|
||||||
if not p.match(doc.gstin):
|
p = re.compile("^[0-9]{4}[A-Z]{3}[0-9]{5}[0-9A-Z]{3}")
|
||||||
frappe.throw(_("Invalid GSTIN! The input you've entered doesn't match the format of GSTIN."))
|
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}$")
|
||||||
|
if not p.match(doc.gstin):
|
||||||
|
frappe.throw(_("Invalid GSTIN! The input you've entered doesn't match the format of GSTIN."))
|
||||||
|
|
||||||
validate_gstin_check_digit(doc.gstin)
|
validate_gstin_check_digit(doc.gstin)
|
||||||
|
|
||||||
if not doc.gst_state:
|
if not doc.gst_state:
|
||||||
if not doc.state:
|
if not doc.state:
|
||||||
return
|
return
|
||||||
state = doc.state.lower()
|
state = doc.state.lower()
|
||||||
states_lowercase = {s.lower():s for s in states}
|
states_lowercase = {s.lower():s for s in states}
|
||||||
if state in states_lowercase:
|
if state in states_lowercase:
|
||||||
doc.gst_state = states_lowercase[state]
|
doc.gst_state = states_lowercase[state]
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
doc.gst_state_number = state_numbers[doc.gst_state]
|
doc.gst_state_number = state_numbers[doc.gst_state]
|
||||||
if doc.gst_state_number != doc.gstin[:2]:
|
if doc.gst_state_number != doc.gstin[:2]:
|
||||||
frappe.throw(_("Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.")
|
frappe.throw(_("Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.")
|
||||||
.format(doc.gst_state_number))
|
.format(doc.gst_state_number))
|
||||||
|
|
||||||
def validate_gstin_check_digit(gstin):
|
def validate_gstin_check_digit(gstin):
|
||||||
''' Function to validate the check digit of the GSTIN.'''
|
''' Function to validate the check digit of the GSTIN.'''
|
||||||
|
0
erpnext/regional/report/datev/__init__.py
Normal file
0
erpnext/regional/report/datev/__init__.py
Normal file
32
erpnext/regional/report/datev/datev.js
Normal file
32
erpnext/regional/report/datev/datev.js
Normal 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}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
29
erpnext/regional/report/datev/datev.json
Normal file
29
erpnext/regional/report/datev/datev.json
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
373
erpnext/regional/report/datev/datev.py
Normal file
373
erpnext/regional/report/datev/datev.py
Normal 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'
|
@ -209,7 +209,10 @@ 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 = json.loads(d.item_tax_rate)
|
item_tax_rate = {}
|
||||||
|
|
||||||
|
if d.item_tax_rate:
|
||||||
|
item_tax_rate = json.loads(d.item_tax_rate)
|
||||||
|
|
||||||
if item_tax_rate:
|
if item_tax_rate:
|
||||||
self.item_tax_rate.setdefault(d.parent, {}).setdefault(d.item_code, item_tax_rate)
|
self.item_tax_rate.setdefault(d.parent, {}).setdefault(d.item_code, item_tax_rate)
|
||||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user