mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-02 11:49:10 +00:00
Merge branch 'hotfix' into fix_by_voucher_order
This commit is contained in:
25
.travis.yml
25
.travis.yml
@@ -3,6 +3,11 @@ dist: trusty
|
|||||||
|
|
||||||
python:
|
python:
|
||||||
- "2.7"
|
- "2.7"
|
||||||
|
- "3.6"
|
||||||
|
|
||||||
|
env:
|
||||||
|
- TEST_TYPE="Server Side Test"
|
||||||
|
- TEST_TYPE="Patch Test"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- mysql
|
- mysql
|
||||||
@@ -39,18 +44,8 @@ before_script:
|
|||||||
- bench start &
|
- bench start &
|
||||||
- sleep 10
|
- sleep 10
|
||||||
|
|
||||||
jobs:
|
script:
|
||||||
include:
|
- bash $TRAVIS_BUILD_DIR/travis/run-tests.sh
|
||||||
- stage: test
|
|
||||||
script:
|
after_script:
|
||||||
- set -e
|
- coveralls -b apps/erpnext -d ../../sites/.coverage
|
||||||
- bench run-tests --app erpnext --coverage
|
|
||||||
after_script:
|
|
||||||
- coveralls -b apps/erpnext -d ../../sites/.coverage
|
|
||||||
env: Server Side Test
|
|
||||||
- # stage
|
|
||||||
script:
|
|
||||||
- wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz
|
|
||||||
- bench --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz --mariadb-root-password travis
|
|
||||||
- bench migrate
|
|
||||||
env: Patch Testing
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import frappe
|
|||||||
from erpnext.hooks import regional_overrides
|
from erpnext.hooks import regional_overrides
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate
|
||||||
|
|
||||||
__version__ = '11.1.38'
|
__version__ = '11.1.39'
|
||||||
|
|
||||||
def get_default_company(user=None):
|
def get_default_company(user=None):
|
||||||
'''Get default company for user'''
|
'''Get default company for user'''
|
||||||
|
|||||||
@@ -43,8 +43,13 @@ frappe.ui.form.on('Bank Guarantee', {
|
|||||||
|
|
||||||
reference_docname: function(frm) {
|
reference_docname: function(frm) {
|
||||||
if (frm.doc.reference_docname && frm.doc.reference_doctype) {
|
if (frm.doc.reference_docname && frm.doc.reference_doctype) {
|
||||||
let fields_to_fetch = ["project", "grand_total"];
|
let fields_to_fetch = ["grand_total"];
|
||||||
let party_field = frm.doc.reference_doctype == "Sales Order" ? "customer" : "supplier";
|
let party_field = frm.doc.reference_doctype == "Sales Order" ? "customer" : "supplier";
|
||||||
|
|
||||||
|
if (frm.doc.reference_doctype == "Sales Order") {
|
||||||
|
fields_to_fetch.push("project");
|
||||||
|
}
|
||||||
|
|
||||||
fields_to_fetch.push(party_field);
|
fields_to_fetch.push(party_field);
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.doctype.bank_guarantee.bank_guarantee.get_vouchar_detials",
|
method: "erpnext.accounts.doctype.bank_guarantee.bank_guarantee.get_vouchar_detials",
|
||||||
|
|||||||
@@ -350,7 +350,7 @@ def filter_pricing_rules(args, pricing_rules):
|
|||||||
if len(pricing_rules) > 1:
|
if len(pricing_rules) > 1:
|
||||||
rate_or_discount = list(set([d.rate_or_discount for d in pricing_rules]))
|
rate_or_discount = list(set([d.rate_or_discount for d in pricing_rules]))
|
||||||
if len(rate_or_discount) == 1 and rate_or_discount[0] == "Discount Percentage":
|
if len(rate_or_discount) == 1 and rate_or_discount[0] == "Discount Percentage":
|
||||||
pricing_rules = filter(lambda x: x.for_price_list==args.price_list, pricing_rules) \
|
pricing_rules = list(filter(lambda x: x.for_price_list==args.price_list, pricing_rules)) \
|
||||||
or pricing_rules
|
or pricing_rules
|
||||||
|
|
||||||
if len(pricing_rules) > 1 and not args.for_shopping_cart:
|
if len(pricing_rules) > 1 and not args.for_shopping_cart:
|
||||||
@@ -373,7 +373,7 @@ def apply_internal_priority(pricing_rules, field_set, args):
|
|||||||
filtered_rules = []
|
filtered_rules = []
|
||||||
for field in field_set:
|
for field in field_set:
|
||||||
if args.get(field):
|
if args.get(field):
|
||||||
filtered_rules = filter(lambda x: x[field]==args[field], pricing_rules)
|
filtered_rules = list(filter(lambda x: x[field]==args[field], pricing_rules))
|
||||||
if filtered_rules: break
|
if filtered_rules: break
|
||||||
|
|
||||||
return filtered_rules or pricing_rules
|
return filtered_rules or pricing_rules
|
||||||
|
|||||||
@@ -371,6 +371,10 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
|||||||
me.frm.pos_print_format = r.message.print_format;
|
me.frm.pos_print_format = r.message.print_format;
|
||||||
}
|
}
|
||||||
me.frm.script_manager.trigger("update_stock");
|
me.frm.script_manager.trigger("update_stock");
|
||||||
|
if(me.frm.doc.taxes_and_charges) {
|
||||||
|
me.frm.script_manager.trigger("taxes_and_charges");
|
||||||
|
}
|
||||||
|
|
||||||
frappe.model.set_default_values(me.frm.doc);
|
frappe.model.set_default_values(me.frm.doc);
|
||||||
me.set_dynamic_labels();
|
me.set_dynamic_labels();
|
||||||
me.calculate_taxes_and_totals();
|
me.calculate_taxes_and_totals();
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
|||||||
invoices = []
|
invoices = []
|
||||||
|
|
||||||
# create invoices for lower than single threshold tax rate
|
# create invoices for lower than single threshold tax rate
|
||||||
for _ in xrange(2):
|
for _ in range(2):
|
||||||
pi = create_purchase_invoice(supplier = "Test TDS Supplier")
|
pi = create_purchase_invoice(supplier = "Test TDS Supplier")
|
||||||
pi.submit()
|
pi.submit()
|
||||||
invoices.append(pi)
|
invoices.append(pi)
|
||||||
|
|||||||
@@ -10,4 +10,10 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"default": 1
|
"default": 1
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frappe.query_reports["Balance Sheet"]["filters"].push({
|
||||||
|
"fieldname": "include_default_book_entries",
|
||||||
|
"label": __("Include Default Book Entries"),
|
||||||
|
"fieldtype": "Check"
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ def execute(filters=None):
|
|||||||
|
|
||||||
if(filters.get("show_cumulative")):
|
if(filters.get("show_cumulative")):
|
||||||
last_total = period_data[0] - period_data[1]
|
last_total = period_data[0] - period_data[1]
|
||||||
|
|
||||||
period_data[2] = period_data[0] - period_data[1]
|
period_data[2] = period_data[0] - period_data[1]
|
||||||
row += period_data
|
row += period_data
|
||||||
totals[2] = totals[0] - totals[1]
|
totals[2] = totals[0] - totals[1]
|
||||||
if filters["period"] != "Yearly" :
|
if filters["period"] != "Yearly" :
|
||||||
@@ -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,20 +79,20 @@ 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
|
||||||
|
|
||||||
def get_cost_centers(filters):
|
def get_cost_centers(filters):
|
||||||
cond = "and 1=1"
|
cond = "and 1=1"
|
||||||
if filters.get("budget_against") == "Cost Center":
|
if filters.get("budget_against") == "Cost Center":
|
||||||
cond = "order by lft"
|
cond = "order by lft"
|
||||||
|
|
||||||
return frappe.db.sql_list("""select name from `tab{tab}` where company=%s
|
return frappe.db.sql_list("""select name from `tab{tab}` where company=%s
|
||||||
{cond}""".format(tab=filters.get("budget_against"), cond=cond), filters.get("company"))
|
{cond}""".format(tab=filters.get("budget_against"), cond=cond), filters.get("company"))
|
||||||
|
|
||||||
#Get cost center & target details
|
#Get cost center & target details
|
||||||
@@ -109,7 +109,7 @@ def get_cost_center_target_details(filters):
|
|||||||
""".format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond),
|
""".format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond),
|
||||||
(filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company), as_dict=True)
|
(filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company), as_dict=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#Get target distribution details of accounts of cost center
|
#Get target distribution details of accounts of cost center
|
||||||
def get_target_distribution_details(filters):
|
def get_target_distribution_details(filters):
|
||||||
@@ -118,7 +118,7 @@ def get_target_distribution_details(filters):
|
|||||||
from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
|
from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
|
||||||
where mdp.parent=md.name and md.fiscal_year between %s and %s order by md.fiscal_year""",(filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1):
|
where mdp.parent=md.name and md.fiscal_year between %s and %s order by md.fiscal_year""",(filters.from_fiscal_year, filters.to_fiscal_year), as_dict=1):
|
||||||
target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation))
|
target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation))
|
||||||
|
|
||||||
return target_details
|
return target_details
|
||||||
|
|
||||||
#Get actual details from gl entry
|
#Get actual details from gl entry
|
||||||
@@ -129,7 +129,7 @@ def get_actual_details(name, filters):
|
|||||||
if filters.get("budget_against") == "Cost Center":
|
if filters.get("budget_against") == "Cost Center":
|
||||||
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"])
|
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"])
|
||||||
cond = "lft>='{lft}' and rgt<='{rgt}'".format(lft = cc_lft, rgt=cc_rgt)
|
cond = "lft>='{lft}' and rgt<='{rgt}'".format(lft = cc_lft, rgt=cc_rgt)
|
||||||
|
|
||||||
ac_details = frappe.db.sql("""select gl.account, gl.debit, gl.credit,gl.fiscal_year,
|
ac_details = frappe.db.sql("""select gl.account, gl.debit, gl.credit,gl.fiscal_year,
|
||||||
MONTHNAME(gl.posting_date) as month_name, b.{budget_against} as budget_against
|
MONTHNAME(gl.posting_date) as month_name, b.{budget_against} as budget_against
|
||||||
from `tabGL Entry` gl, `tabBudget Account` ba, `tabBudget` b
|
from `tabGL Entry` gl, `tabBudget Account` ba, `tabBudget` b
|
||||||
@@ -159,7 +159,7 @@ def get_cost_center_account_month_map(filters):
|
|||||||
|
|
||||||
for ccd in cost_center_target_details:
|
for ccd in cost_center_target_details:
|
||||||
actual_details = get_actual_details(ccd.budget_against, filters)
|
actual_details = get_actual_details(ccd.budget_against, filters)
|
||||||
|
|
||||||
for month_id in range(1, 13):
|
for month_id in range(1, 13):
|
||||||
month = datetime.date(2013, month_id, 1).strftime('%B')
|
month = datetime.date(2013, month_id, 1).strftime('%B')
|
||||||
cam_map.setdefault(ccd.budget_against, {}).setdefault(ccd.account, {}).setdefault(ccd.fiscal_year,{})\
|
cam_map.setdefault(ccd.budget_against, {}).setdefault(ccd.account, {}).setdefault(ccd.fiscal_year,{})\
|
||||||
@@ -172,7 +172,7 @@ def get_cost_center_account_month_map(filters):
|
|||||||
if ccd.monthly_distribution else 100.0/12
|
if ccd.monthly_distribution else 100.0/12
|
||||||
|
|
||||||
tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100
|
tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100
|
||||||
|
|
||||||
for ad in actual_details.get(ccd.account, []):
|
for ad in actual_details.get(ccd.account, []):
|
||||||
if ad.month_name == month:
|
if ad.month_name == month:
|
||||||
tav_dict.actual += flt(ad.debit) - flt(ad.credit)
|
tav_dict.actual += flt(ad.debit) - flt(ad.credit)
|
||||||
|
|||||||
@@ -15,4 +15,10 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
"label": __("Accumulated Values"),
|
"label": __("Accumulated Values"),
|
||||||
"fieldtype": "Check"
|
"fieldtype": "Check"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frappe.query_reports["Cash Flow"]["filters"].push({
|
||||||
|
"fieldname": "include_default_book_entries",
|
||||||
|
"label": __("Include Default Book Entries"),
|
||||||
|
"fieldtype": "Check"
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -14,8 +14,8 @@ def execute(filters=None):
|
|||||||
if cint(frappe.db.get_single_value('Accounts Settings', 'use_custom_cash_flow')):
|
if cint(frappe.db.get_single_value('Accounts Settings', 'use_custom_cash_flow')):
|
||||||
from erpnext.accounts.report.cash_flow.custom_cash_flow import execute as execute_custom
|
from erpnext.accounts.report.cash_flow.custom_cash_flow import execute as execute_custom
|
||||||
return execute_custom(filters=filters)
|
return execute_custom(filters=filters)
|
||||||
|
|
||||||
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
|
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
|
||||||
filters.periodicity, filters.accumulated_values, filters.company)
|
filters.periodicity, filters.accumulated_values, filters.company)
|
||||||
|
|
||||||
cash_flow_accounts = get_cash_flow_accounts()
|
cash_flow_accounts = get_cash_flow_accounts()
|
||||||
@@ -25,18 +25,18 @@ def execute(filters=None):
|
|||||||
accumulated_values=filters.accumulated_values, ignore_closing_entries=True, ignore_accumulated_values_for_fy= True)
|
accumulated_values=filters.accumulated_values, ignore_closing_entries=True, ignore_accumulated_values_for_fy= True)
|
||||||
expense = get_data(filters.company, "Expense", "Debit", period_list, filters=filters,
|
expense = get_data(filters.company, "Expense", "Debit", period_list, filters=filters,
|
||||||
accumulated_values=filters.accumulated_values, ignore_closing_entries=True, ignore_accumulated_values_for_fy= True)
|
accumulated_values=filters.accumulated_values, ignore_closing_entries=True, ignore_accumulated_values_for_fy= True)
|
||||||
|
|
||||||
net_profit_loss = get_net_profit_loss(income, expense, period_list, filters.company)
|
net_profit_loss = get_net_profit_loss(income, expense, period_list, filters.company)
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
company_currency = frappe.get_cached_value('Company', filters.company, "default_currency")
|
company_currency = frappe.get_cached_value('Company', filters.company, "default_currency")
|
||||||
|
|
||||||
for cash_flow_account in cash_flow_accounts:
|
for cash_flow_account in cash_flow_accounts:
|
||||||
section_data = []
|
section_data = []
|
||||||
data.append({
|
data.append({
|
||||||
"account_name": cash_flow_account['section_header'],
|
"account_name": cash_flow_account['section_header'],
|
||||||
"parent_account": None,
|
"parent_account": None,
|
||||||
"indent": 0.0,
|
"indent": 0.0,
|
||||||
"account": cash_flow_account['section_header']
|
"account": cash_flow_account['section_header']
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -44,18 +44,18 @@ def execute(filters=None):
|
|||||||
# add first net income in operations section
|
# add first net income in operations section
|
||||||
if net_profit_loss:
|
if net_profit_loss:
|
||||||
net_profit_loss.update({
|
net_profit_loss.update({
|
||||||
"indent": 1,
|
"indent": 1,
|
||||||
"parent_account": cash_flow_accounts[0]['section_header']
|
"parent_account": cash_flow_accounts[0]['section_header']
|
||||||
})
|
})
|
||||||
data.append(net_profit_loss)
|
data.append(net_profit_loss)
|
||||||
section_data.append(net_profit_loss)
|
section_data.append(net_profit_loss)
|
||||||
|
|
||||||
for account in cash_flow_account['account_types']:
|
for account in cash_flow_account['account_types']:
|
||||||
account_data = get_account_type_based_data(filters.company,
|
account_data = get_account_type_based_data(filters.company,
|
||||||
account['account_type'], period_list, filters.accumulated_values)
|
account['account_type'], period_list, filters.accumulated_values, filters)
|
||||||
account_data.update({
|
account_data.update({
|
||||||
"account_name": account['label'],
|
"account_name": account['label'],
|
||||||
"account": account['label'],
|
"account": account['label'],
|
||||||
"indent": 1,
|
"indent": 1,
|
||||||
"parent_account": cash_flow_account['section_header'],
|
"parent_account": cash_flow_account['section_header'],
|
||||||
"currency": company_currency
|
"currency": company_currency
|
||||||
@@ -63,7 +63,7 @@ def execute(filters=None):
|
|||||||
data.append(account_data)
|
data.append(account_data)
|
||||||
section_data.append(account_data)
|
section_data.append(account_data)
|
||||||
|
|
||||||
add_total_row_account(data, section_data, cash_flow_account['section_footer'],
|
add_total_row_account(data, section_data, cash_flow_account['section_footer'],
|
||||||
period_list, company_currency)
|
period_list, company_currency)
|
||||||
|
|
||||||
add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency)
|
add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency)
|
||||||
@@ -105,13 +105,15 @@ def get_cash_flow_accounts():
|
|||||||
# combine all cash flow accounts for iteration
|
# combine all cash flow accounts for iteration
|
||||||
return [operation_accounts, investing_accounts, financing_accounts]
|
return [operation_accounts, investing_accounts, financing_accounts]
|
||||||
|
|
||||||
def get_account_type_based_data(company, account_type, period_list, accumulated_values):
|
def get_account_type_based_data(company, account_type, period_list, accumulated_values, filters):
|
||||||
data = {}
|
data = {}
|
||||||
total = 0
|
total = 0
|
||||||
for period in period_list:
|
for period in period_list:
|
||||||
start_date = get_start_date(period, accumulated_values, company)
|
start_date = get_start_date(period, accumulated_values, company)
|
||||||
|
|
||||||
amount = get_account_type_based_gl_data(company, start_date, period['to_date'], account_type)
|
amount = get_account_type_based_gl_data(company, start_date,
|
||||||
|
period['to_date'], account_type, filters)
|
||||||
|
|
||||||
if amount and account_type == "Depreciation":
|
if amount and account_type == "Depreciation":
|
||||||
amount *= -1
|
amount *= -1
|
||||||
|
|
||||||
@@ -121,14 +123,24 @@ def get_account_type_based_data(company, account_type, period_list, accumulated_
|
|||||||
data["total"] = total
|
data["total"] = total
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_account_type_based_gl_data(company, start_date, end_date, account_type):
|
def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters):
|
||||||
|
cond = ""
|
||||||
|
|
||||||
|
if filters.finance_book:
|
||||||
|
cond = " and finance_book = '%s'" %(frappe.db.escape(filters.finance_book))
|
||||||
|
if filters.include_default_book_entries:
|
||||||
|
company_fb = frappe.db.get_value("Company", company, 'default_finance_book')
|
||||||
|
|
||||||
|
cond = """ and finance_book in ('%s', '%s')
|
||||||
|
""" %(frappe.db.escape(filters.finance_book), frappe.db.escape(company_fb))
|
||||||
|
|
||||||
gl_sum = frappe.db.sql_list("""
|
gl_sum = frappe.db.sql_list("""
|
||||||
select sum(credit) - sum(debit)
|
select sum(credit) - sum(debit)
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where company=%s and posting_date >= %s and posting_date <= %s
|
where company=%s and posting_date >= %s and posting_date <= %s
|
||||||
and voucher_type != 'Period Closing Voucher'
|
and voucher_type != 'Period Closing Voucher'
|
||||||
and account in ( SELECT name FROM tabAccount WHERE account_type = %s)
|
and account in ( SELECT name FROM tabAccount WHERE account_type = %s) {cond}
|
||||||
""", (company, start_date, end_date, account_type))
|
""".format(cond=cond), (company, start_date, end_date, account_type))
|
||||||
|
|
||||||
return gl_sum[0] if gl_sum and gl_sum[0] else 0
|
return gl_sum[0] if gl_sum and gl_sum[0] else 0
|
||||||
|
|
||||||
@@ -154,7 +166,7 @@ def add_total_row_account(out, data, label, period_list, currency, consolidated
|
|||||||
key = period if consolidated else period['key']
|
key = period if consolidated else period['key']
|
||||||
total_row.setdefault(key, 0.0)
|
total_row.setdefault(key, 0.0)
|
||||||
total_row[key] += row.get(key, 0.0)
|
total_row[key] += row.get(key, 0.0)
|
||||||
|
|
||||||
total_row.setdefault("total", 0.0)
|
total_row.setdefault("total", 0.0)
|
||||||
total_row["total"] += row["total"]
|
total_row["total"] += row["total"]
|
||||||
|
|
||||||
|
|||||||
@@ -55,5 +55,10 @@ frappe.query_reports["Consolidated Financial Statement"] = {
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"default": 0
|
"default": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "include_default_book_entries",
|
||||||
|
"label": __("Include Default Book Entries"),
|
||||||
|
"fieldtype": "Check"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -355,7 +355,8 @@ def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, g
|
|||||||
"lft": root_lft,
|
"lft": root_lft,
|
||||||
"rgt": root_rgt,
|
"rgt": root_rgt,
|
||||||
"company": d.name,
|
"company": d.name,
|
||||||
"finance_book": filters.get("finance_book")
|
"finance_book": filters.get("finance_book"),
|
||||||
|
"company_fb": frappe.db.get_value("Company", d.name, 'default_finance_book')
|
||||||
},
|
},
|
||||||
as_dict=True)
|
as_dict=True)
|
||||||
|
|
||||||
@@ -386,7 +387,10 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters):
|
|||||||
additional_conditions.append("gl.posting_date >= %(from_date)s")
|
additional_conditions.append("gl.posting_date >= %(from_date)s")
|
||||||
|
|
||||||
if filters.get("finance_book"):
|
if filters.get("finance_book"):
|
||||||
additional_conditions.append("ifnull(finance_book, '') in (%(finance_book)s, '')")
|
if filters.get("include_default_book_entries"):
|
||||||
|
additional_conditions.append("finance_book in (%(finance_book)s, %(company_fb)s)")
|
||||||
|
else:
|
||||||
|
additional_conditions.append("finance_book in (%(finance_book)s)")
|
||||||
|
|
||||||
return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""
|
return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""
|
||||||
|
|
||||||
|
|||||||
@@ -359,7 +359,8 @@ def set_gl_entries_by_account(
|
|||||||
"to_date": to_date,
|
"to_date": to_date,
|
||||||
"cost_center": filters.cost_center,
|
"cost_center": filters.cost_center,
|
||||||
"project": filters.project,
|
"project": filters.project,
|
||||||
"finance_book": filters.get("finance_book")
|
"finance_book": filters.get("finance_book"),
|
||||||
|
"company_fb": frappe.db.get_value("Company", company, 'default_finance_book')
|
||||||
},
|
},
|
||||||
as_dict=True)
|
as_dict=True)
|
||||||
|
|
||||||
@@ -393,7 +394,10 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters):
|
|||||||
additional_conditions.append("cost_center in %(cost_center)s")
|
additional_conditions.append("cost_center in %(cost_center)s")
|
||||||
|
|
||||||
if filters.get("finance_book"):
|
if filters.get("finance_book"):
|
||||||
additional_conditions.append("ifnull(finance_book, '') in (%(finance_book)s, '')")
|
if filters.get("include_default_book_entries"):
|
||||||
|
additional_conditions.append("finance_book in (%(finance_book)s, %(company_fb)s)")
|
||||||
|
else:
|
||||||
|
additional_conditions.append("finance_book in (%(finance_book)s)")
|
||||||
|
|
||||||
return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""
|
return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""
|
||||||
|
|
||||||
|
|||||||
@@ -216,6 +216,11 @@ frappe.query_reports["General Ledger"] = {
|
|||||||
"fieldname": "show_opening_entries",
|
"fieldname": "show_opening_entries",
|
||||||
"label": __("Show Opening Entries"),
|
"label": __("Show Opening Entries"),
|
||||||
"fieldtype": "Check"
|
"fieldtype": "Check"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "include_default_book_entries",
|
||||||
|
"label": __("Include Default Book Entries"),
|
||||||
|
"fieldtype": "Check"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,13 +128,16 @@ def get_gl_entries(filters):
|
|||||||
order_by_statement = "order by posting_date, voucher_type, voucher_no"
|
order_by_statement = "order by posting_date, voucher_type, voucher_no"
|
||||||
|
|
||||||
if filters.get("group_by") == _("Group by Voucher (Consolidated)"):
|
if filters.get("group_by") == _("Group by Voucher (Consolidated)"):
|
||||||
group_by_statement = """group by voucher_type, voucher_no, account,
|
group_by_statement = "group by voucher_type, voucher_no, account, cost_center"
|
||||||
cost_center, against_voucher_type, against_voucher, posting_date"""
|
|
||||||
|
|
||||||
select_fields = """, sum(debit) as debit, sum(credit) as credit,
|
select_fields = """, sum(debit) as debit, sum(credit) as credit,
|
||||||
sum(debit_in_account_currency) as debit_in_account_currency,
|
sum(debit_in_account_currency) as debit_in_account_currency,
|
||||||
sum(credit_in_account_currency) as credit_in_account_currency"""
|
sum(credit_in_account_currency) as credit_in_account_currency"""
|
||||||
|
|
||||||
|
if filters.get("include_default_book_entries"):
|
||||||
|
filters['company_fb'] = frappe.db.get_value("Company",
|
||||||
|
filters.get("company"), 'default_finance_book')
|
||||||
|
|
||||||
gl_entries = frappe.db.sql(
|
gl_entries = frappe.db.sql(
|
||||||
"""
|
"""
|
||||||
select
|
select
|
||||||
@@ -190,7 +193,10 @@ def get_conditions(filters):
|
|||||||
conditions.append("project in %(project)s")
|
conditions.append("project in %(project)s")
|
||||||
|
|
||||||
if filters.get("finance_book"):
|
if filters.get("finance_book"):
|
||||||
conditions.append("ifnull(finance_book, '') in (%(finance_book)s, '')")
|
if filters.get("include_default_book_entries"):
|
||||||
|
conditions.append("finance_book in (%(finance_book)s, %(company_fb)s)")
|
||||||
|
else:
|
||||||
|
conditions.append("finance_book in (%(finance_book)s)")
|
||||||
|
|
||||||
from frappe.desk.reportview import build_match_conditions
|
from frappe.desk.reportview import build_match_conditions
|
||||||
match_conditions = build_match_conditions("GL Entry")
|
match_conditions = build_match_conditions("GL Entry")
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
"fieldname": "accumulated_values",
|
"fieldname": "accumulated_values",
|
||||||
"label": __("Accumulated Values"),
|
"label": __("Accumulated Values"),
|
||||||
"fieldtype": "Check"
|
"fieldtype": "Check"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "include_default_book_entries",
|
||||||
|
"label": __("Include Default Book Entries"),
|
||||||
|
"fieldtype": "Check"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ class TestSalesPaymentSummary(unittest.TestCase):
|
|||||||
pe.submit()
|
pe.submit()
|
||||||
|
|
||||||
mop = get_mode_of_payments(filters)
|
mop = get_mode_of_payments(filters)
|
||||||
self.assertTrue('Credit Card' in mop.values()[0])
|
self.assertTrue('Credit Card' in list(mop.values())[0])
|
||||||
self.assertTrue('Cash' in mop.values()[0])
|
self.assertTrue('Cash' in list(mop.values())[0])
|
||||||
|
|
||||||
# Cancel all Cash payment entry and check if this mode of payment is still fetched.
|
# Cancel all Cash payment entry and check if this mode of payment is still fetched.
|
||||||
payment_entries = frappe.get_all("Payment Entry", filters={"mode_of_payment": "Cash", "docstatus": 1}, fields=["name", "docstatus"])
|
payment_entries = frappe.get_all("Payment Entry", filters={"mode_of_payment": "Cash", "docstatus": 1}, fields=["name", "docstatus"])
|
||||||
@@ -57,8 +57,8 @@ class TestSalesPaymentSummary(unittest.TestCase):
|
|||||||
pe.cancel()
|
pe.cancel()
|
||||||
|
|
||||||
mop = get_mode_of_payments(filters)
|
mop = get_mode_of_payments(filters)
|
||||||
self.assertTrue('Credit Card' in mop.values()[0])
|
self.assertTrue('Credit Card' in list(mop.values())[0])
|
||||||
self.assertTrue('Cash' not in mop.values()[0])
|
self.assertTrue('Cash' not in list(mop.values())[0])
|
||||||
|
|
||||||
def test_get_mode_of_payments_details(self):
|
def test_get_mode_of_payments_details(self):
|
||||||
filters = get_filters()
|
filters = get_filters()
|
||||||
@@ -84,7 +84,7 @@ class TestSalesPaymentSummary(unittest.TestCase):
|
|||||||
|
|
||||||
mopd = get_mode_of_payment_details(filters)
|
mopd = get_mode_of_payment_details(filters)
|
||||||
|
|
||||||
mopd_values = mopd.values()[0]
|
mopd_values = list(mopd.values())[0]
|
||||||
for mopd_value in mopd_values:
|
for mopd_value in mopd_values:
|
||||||
if mopd_value[0] == "Credit Card":
|
if mopd_value[0] == "Credit Card":
|
||||||
cc_init_amount = mopd_value[1]
|
cc_init_amount = mopd_value[1]
|
||||||
@@ -96,7 +96,7 @@ class TestSalesPaymentSummary(unittest.TestCase):
|
|||||||
pe.cancel()
|
pe.cancel()
|
||||||
|
|
||||||
mopd = get_mode_of_payment_details(filters)
|
mopd = get_mode_of_payment_details(filters)
|
||||||
mopd_values = mopd.values()[0]
|
mopd_values = list(mopd.values())[0]
|
||||||
for mopd_value in mopd_values:
|
for mopd_value in mopd_values:
|
||||||
if mopd_value[0] == "Credit Card":
|
if mopd_value[0] == "Credit Card":
|
||||||
cc_final_amount = mopd_value[1]
|
cc_final_amount = mopd_value[1]
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ def get_rootwise_opening_balances(filters, report_type):
|
|||||||
if filters.finance_book:
|
if filters.finance_book:
|
||||||
fb_conditions = " and finance_book = %(finance_book)s"
|
fb_conditions = " and finance_book = %(finance_book)s"
|
||||||
if filters.include_default_book_entries:
|
if filters.include_default_book_entries:
|
||||||
fb_conditions = " and (finance_book in (%(finance_book)s, %(company_fb)s) or finance_book is null)"
|
fb_conditions = " and (finance_book in (%(finance_book)s, %(company_fb)s))"
|
||||||
|
|
||||||
additional_conditions += fb_conditions
|
additional_conditions += fb_conditions
|
||||||
|
|
||||||
@@ -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
|
|
||||||
|
|||||||
@@ -291,16 +291,19 @@ class Asset(AccountsController):
|
|||||||
|
|
||||||
def validate_expected_value_after_useful_life(self):
|
def validate_expected_value_after_useful_life(self):
|
||||||
for row in self.get('finance_books'):
|
for row in self.get('finance_books'):
|
||||||
accumulated_depreciation_after_full_schedule = max([d.accumulated_depreciation_amount
|
accumulated_depreciation_after_full_schedule = [d.accumulated_depreciation_amount
|
||||||
for d in self.get("schedules") if cint(d.finance_book_id) == row.idx])
|
for d in self.get("schedules") if cint(d.finance_book_id) == row.idx]
|
||||||
|
|
||||||
asset_value_after_full_schedule = flt(flt(self.gross_purchase_amount) -
|
if accumulated_depreciation_after_full_schedule:
|
||||||
flt(accumulated_depreciation_after_full_schedule),
|
accumulated_depreciation_after_full_schedule = max(accumulated_depreciation_after_full_schedule)
|
||||||
self.precision('gross_purchase_amount'))
|
|
||||||
|
|
||||||
if row.expected_value_after_useful_life < asset_value_after_full_schedule:
|
asset_value_after_full_schedule = flt(flt(self.gross_purchase_amount) -
|
||||||
frappe.throw(_("Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1}")
|
flt(accumulated_depreciation_after_full_schedule),
|
||||||
.format(row.idx, asset_value_after_full_schedule))
|
self.precision('gross_purchase_amount'))
|
||||||
|
|
||||||
|
if row.expected_value_after_useful_life < asset_value_after_full_schedule:
|
||||||
|
frappe.throw(_("Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1}")
|
||||||
|
.format(row.idx, asset_value_after_full_schedule))
|
||||||
|
|
||||||
def validate_cancellation(self):
|
def validate_cancellation(self):
|
||||||
if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"):
|
if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"):
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class AssetValueAdjustment(Document):
|
|||||||
fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \
|
fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \
|
||||||
get_depreciation_accounts(asset)
|
get_depreciation_accounts(asset)
|
||||||
|
|
||||||
depreciation_cost_center, depreciation_series = frappe.get_cached_value('Company', asset.company,
|
depreciation_cost_center, depreciation_series = frappe.get_cached_value('Company', asset.company,
|
||||||
["depreciation_cost_center", "series_for_depreciation_entry"])
|
["depreciation_cost_center", "series_for_depreciation_entry"])
|
||||||
|
|
||||||
je = frappe.new_doc("Journal Entry")
|
je = frappe.new_doc("Journal Entry")
|
||||||
@@ -75,8 +75,8 @@ class AssetValueAdjustment(Document):
|
|||||||
rate_per_day = flt(d.value_after_depreciation) / flt(total_days)
|
rate_per_day = flt(d.value_after_depreciation) / flt(total_days)
|
||||||
from_date = self.date
|
from_date = self.date
|
||||||
else:
|
else:
|
||||||
no_of_depreciations = len([e.name for e in asset.schedules
|
no_of_depreciations = len([s.name for s in asset.schedules
|
||||||
if (cint(s.finance_book_id) == d.idx and not e.journal_entry)])
|
if (cint(s.finance_book_id) == d.idx and not s.journal_entry)])
|
||||||
|
|
||||||
value_after_depreciation = d.value_after_depreciation
|
value_after_depreciation = d.value_after_depreciation
|
||||||
for data in asset.schedules:
|
for data in asset.schedules:
|
||||||
|
|||||||
@@ -25,9 +25,12 @@ class TestLocation(unittest.TestCase):
|
|||||||
temp['features'][0]['properties']['feature_of'] = location
|
temp['features'][0]['properties']['feature_of'] = location
|
||||||
formatted_locations.extend(temp['features'])
|
formatted_locations.extend(temp['features'])
|
||||||
|
|
||||||
formatted_location_string = str(formatted_locations)
|
|
||||||
test_location = frappe.get_doc('Location', 'Test Location Area')
|
test_location = frappe.get_doc('Location', 'Test Location Area')
|
||||||
test_location.save()
|
test_location.save()
|
||||||
|
|
||||||
self.assertEqual(formatted_location_string, str(json.loads(test_location.get('location'))['features']))
|
test_location_features = json.loads(test_location.get('location'))['features']
|
||||||
|
ordered_test_location_features = sorted(test_location_features, key=lambda x: x['properties']['feature_of'])
|
||||||
|
ordered_formatted_locations = sorted(formatted_locations, key=lambda x: x['properties']['feature_of'])
|
||||||
|
|
||||||
|
self.assertEqual(ordered_formatted_locations, ordered_test_location_features)
|
||||||
self.assertEqual(area, test_location.get('area'))
|
self.assertEqual(area, test_location.get('area'))
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ def get_products_details():
|
|||||||
products_response = call_mws_method(products.get_matching_product,marketplaceid=marketplace,
|
products_response = call_mws_method(products.get_matching_product,marketplaceid=marketplace,
|
||||||
asins=asin_list)
|
asins=asin_list)
|
||||||
|
|
||||||
matching_products_list = products_response.parsed
|
matching_products_list = products_response.parsed
|
||||||
for product in matching_products_list:
|
for product in matching_products_list:
|
||||||
skus = [row["sku"] for row in sku_asin if row["asin"]==product.ASIN]
|
skus = [row["sku"] for row in sku_asin if row["asin"]==product.ASIN]
|
||||||
for sku in skus:
|
for sku in skus:
|
||||||
@@ -116,7 +116,7 @@ def call_mws_method(mws_method, *args, **kwargs):
|
|||||||
mws_settings = frappe.get_doc("Amazon MWS Settings")
|
mws_settings = frappe.get_doc("Amazon MWS Settings")
|
||||||
max_retries = mws_settings.max_retry_limit
|
max_retries = mws_settings.max_retry_limit
|
||||||
|
|
||||||
for x in xrange(0, max_retries):
|
for x in range(0, max_retries):
|
||||||
try:
|
try:
|
||||||
response = mws_method(*args, **kwargs)
|
response = mws_method(*args, **kwargs)
|
||||||
return response
|
return response
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ frappe.ui.form.on("Employee Attendance Tool", {
|
|||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
frm.disable_save();
|
frm.disable_save();
|
||||||
},
|
},
|
||||||
|
|
||||||
onload: function(frm) {
|
onload: function(frm) {
|
||||||
frm.doc.department = frm.doc.branch = frm.doc.company = "All";
|
|
||||||
frm.set_value("date", frappe.datetime.get_today());
|
frm.set_value("date", frappe.datetime.get_today());
|
||||||
erpnext.employee_attendance_tool.load_employees(frm);
|
erpnext.employee_attendance_tool.load_employees(frm);
|
||||||
},
|
},
|
||||||
@@ -24,7 +23,7 @@ frappe.ui.form.on("Employee Attendance Tool", {
|
|||||||
company: function(frm) {
|
company: function(frm) {
|
||||||
erpnext.employee_attendance_tool.load_employees(frm);
|
erpnext.employee_attendance_tool.load_employees(frm);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,12 +17,11 @@ def get_employees(date, department = None, branch = None, company = None):
|
|||||||
attendance_not_marked = []
|
attendance_not_marked = []
|
||||||
attendance_marked = []
|
attendance_marked = []
|
||||||
filters = {"status": "Active", "date_of_joining": ["<=", date]}
|
filters = {"status": "Active", "date_of_joining": ["<=", date]}
|
||||||
if department != "All":
|
|
||||||
filters["department"] = department
|
for field, value in {'department': department,
|
||||||
if branch != "All":
|
'branch': branch, 'company': company}.items():
|
||||||
filters["branch"] = branch
|
if value:
|
||||||
if company != "All":
|
filters[field] = value
|
||||||
filters["company"] = company
|
|
||||||
|
|
||||||
employee_list = frappe.get_list("Employee", fields=["employee", "employee_name"], filters=filters, order_by="employee_name")
|
employee_list = frappe.get_list("Employee", fields=["employee", "employee_name"], filters=filters, order_by="employee_name")
|
||||||
marked_employee = {}
|
marked_employee = {}
|
||||||
|
|||||||
@@ -2,20 +2,8 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Employee Benefit Application', {
|
frappe.ui.form.on('Employee Benefit Application', {
|
||||||
setup: function(frm) {
|
|
||||||
if(!frm.doc.employee || !frm.doc.date) {
|
|
||||||
frappe.throw(__("Please select Employee and Date first"));
|
|
||||||
} else {
|
|
||||||
frm.set_query("earning_component", "employee_benefits", function() {
|
|
||||||
return {
|
|
||||||
query : "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_earning_components",
|
|
||||||
filters: {date: frm.doc.date, employee: frm.doc.employee}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
employee: function(frm) {
|
employee: function(frm) {
|
||||||
|
frm.trigger('set_earning_component');
|
||||||
var method, args;
|
var method, args;
|
||||||
if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
|
if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
|
||||||
method = "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining";
|
method = "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining";
|
||||||
@@ -35,6 +23,21 @@ frappe.ui.form.on('Employee Benefit Application', {
|
|||||||
get_max_benefits(frm, method, args);
|
get_max_benefits(frm, method, args);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
date: function(frm) {
|
||||||
|
frm.trigger('set_earning_component');
|
||||||
|
},
|
||||||
|
|
||||||
|
set_earning_component: function(frm) {
|
||||||
|
if(!frm.doc.employee && !frm.doc.date) return;
|
||||||
|
frm.set_query("earning_component", "employee_benefits", function() {
|
||||||
|
return {
|
||||||
|
query : "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_earning_components",
|
||||||
|
filters: {date: frm.doc.date, employee: frm.doc.employee}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
payroll_period: function(frm) {
|
payroll_period: function(frm) {
|
||||||
var method, args;
|
var method, args;
|
||||||
if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
|
if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
|
||||||
|
|||||||
@@ -445,6 +445,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
|
||||||
|
|||||||
@@ -19,12 +19,12 @@ def execute(filters=None):
|
|||||||
data = []
|
data = []
|
||||||
for ss in salary_slips:
|
for ss in salary_slips:
|
||||||
row = [ss.name, ss.employee, ss.employee_name, ss.branch, ss.department, ss.designation,
|
row = [ss.name, ss.employee, ss.employee_name, ss.branch, ss.department, ss.designation,
|
||||||
ss.company, ss.start_date, ss.end_date, ss.leave_withut_pay, ss.payment_days]
|
ss.company, ss.start_date, ss.end_date, ss.leave_without_pay, ss.payment_days]
|
||||||
|
|
||||||
if not ss.branch == None:columns[3] = columns[3].replace('-1','120')
|
if not ss.branch == None:columns[3] = columns[3].replace('-1','120')
|
||||||
if not ss.department == None: columns[4] = columns[4].replace('-1','120')
|
if not ss.department == None: columns[4] = columns[4].replace('-1','120')
|
||||||
if not ss.designation == None: columns[5] = columns[5].replace('-1','120')
|
if not ss.designation == None: columns[5] = columns[5].replace('-1','120')
|
||||||
if not ss.leave_withut_pay == None: columns[9] = columns[9].replace('-1','130')
|
if not ss.leave_without_pay == None: columns[9] = columns[9].replace('-1','130')
|
||||||
|
|
||||||
|
|
||||||
for e in earning_types:
|
for e in earning_types:
|
||||||
@@ -117,4 +117,4 @@ def get_ss_ded_map(salary_slips):
|
|||||||
ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, [])
|
ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, [])
|
||||||
ss_ded_map[d.parent][d.salary_component] = flt(d.amount)
|
ss_ded_map[d.parent][d.salary_component] = flt(d.amount)
|
||||||
|
|
||||||
return ss_ded_map
|
return ss_ded_map
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty
|
|||||||
from frappe.utils.csvutils import getlink
|
from frappe.utils.csvutils import getlink
|
||||||
from erpnext.stock.utils import get_bin, validate_warehouse_company, get_latest_stock_qty
|
from erpnext.stock.utils import get_bin, validate_warehouse_company, get_latest_stock_qty
|
||||||
from erpnext.utilities.transaction_base import validate_uom_is_integer
|
from erpnext.utilities.transaction_base import validate_uom_is_integer
|
||||||
|
from six import text_type
|
||||||
|
|
||||||
class OverProductionError(frappe.ValidationError): pass
|
class OverProductionError(frappe.ValidationError): pass
|
||||||
class StockOverProductionError(frappe.ValidationError): pass
|
class StockOverProductionError(frappe.ValidationError): pass
|
||||||
@@ -591,10 +592,10 @@ def make_timesheet(production_order, company):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def add_timesheet_detail(timesheet, args):
|
def add_timesheet_detail(timesheet, args):
|
||||||
if isinstance(timesheet, unicode):
|
if isinstance(timesheet, text_type):
|
||||||
timesheet = frappe.get_doc('Timesheet', timesheet)
|
timesheet = frappe.get_doc('Timesheet', timesheet)
|
||||||
|
|
||||||
if isinstance(args, unicode):
|
if isinstance(args, text_type):
|
||||||
args = json.loads(args)
|
args = json.loads(args)
|
||||||
|
|
||||||
timesheet.append('time_logs', args)
|
timesheet.append('time_logs', args)
|
||||||
|
|||||||
@@ -533,7 +533,7 @@ erpnext.patches.v11_0.create_department_records_for_each_company
|
|||||||
erpnext.patches.v11_0.make_location_from_warehouse
|
erpnext.patches.v11_0.make_location_from_warehouse
|
||||||
erpnext.patches.v11_0.make_asset_finance_book_against_old_entries
|
erpnext.patches.v11_0.make_asset_finance_book_against_old_entries
|
||||||
erpnext.patches.v11_0.check_buying_selling_in_currency_exchange
|
erpnext.patches.v11_0.check_buying_selling_in_currency_exchange
|
||||||
erpnext.patches.v11_0.move_item_defaults_to_child_table_for_multicompany #02-07-2018
|
erpnext.patches.v11_0.move_item_defaults_to_child_table_for_multicompany #02-07-2018 #19-06-2019
|
||||||
erpnext.patches.v11_0.refactor_erpnext_shopify #2018-09-07
|
erpnext.patches.v11_0.refactor_erpnext_shopify #2018-09-07
|
||||||
erpnext.patches.v11_0.rename_overproduction_percent_field
|
erpnext.patches.v11_0.rename_overproduction_percent_field
|
||||||
erpnext.patches.v11_0.update_backflush_subcontract_rm_based_on_bom
|
erpnext.patches.v11_0.update_backflush_subcontract_rm_based_on_bom
|
||||||
|
|||||||
@@ -17,10 +17,8 @@ def execute():
|
|||||||
frappe.reload_doc('stock', 'doctype', 'item_default')
|
frappe.reload_doc('stock', 'doctype', 'item_default')
|
||||||
frappe.reload_doc('stock', 'doctype', 'item')
|
frappe.reload_doc('stock', 'doctype', 'item')
|
||||||
|
|
||||||
if frappe.db.a_row_exists('Item Default'): return
|
|
||||||
|
|
||||||
companies = frappe.get_all("Company")
|
companies = frappe.get_all("Company")
|
||||||
if len(companies) == 1:
|
if len(companies) == 1 and not frappe.get_all("Item Default", limit=1):
|
||||||
try:
|
try:
|
||||||
frappe.db.sql('''
|
frappe.db.sql('''
|
||||||
INSERT INTO `tabItem Default`
|
INSERT INTO `tabItem Default`
|
||||||
@@ -35,32 +33,64 @@ def execute():
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
item_details = frappe.get_all("Item", fields=["name", "default_warehouse", "buying_cost_center",
|
item_details = frappe.db.sql(""" SELECT name, default_warehouse,
|
||||||
"expense_account", "selling_cost_center", "income_account"], limit=100)
|
buying_cost_center, expense_account, selling_cost_center, income_account
|
||||||
|
FROM tabItem
|
||||||
|
WHERE
|
||||||
|
name not in (select distinct parent from `tabItem Default`) and ifnull(disabled, 0) = 0"""
|
||||||
|
, as_dict=1)
|
||||||
|
|
||||||
for item in item_details:
|
items_default_data = {}
|
||||||
item_defaults = []
|
for item_data in item_details:
|
||||||
|
for d in [["default_warehouse", "Warehouse"], ["expense_account", "Account"],
|
||||||
|
["income_account", "Account"], ["buying_cost_center", "Cost Center"],
|
||||||
|
["selling_cost_center", "Cost Center"]]:
|
||||||
|
if item_data.get(d[0]):
|
||||||
|
company = frappe.get_value(d[1], item_data.get(d[0]), "company", cache=True)
|
||||||
|
|
||||||
def insert_into_item_defaults(doc_field_name, doc_field_value, company):
|
if item_data.name not in items_default_data:
|
||||||
for d in item_defaults:
|
items_default_data[item_data.name] = {}
|
||||||
if d.get("company") == company:
|
|
||||||
d[doc_field_name] = doc_field_value
|
|
||||||
return
|
|
||||||
item_defaults.append({
|
|
||||||
"company": company,
|
|
||||||
doc_field_name: doc_field_value
|
|
||||||
})
|
|
||||||
|
|
||||||
for d in [
|
company_wise_data = items_default_data[item_data.name]
|
||||||
["default_warehouse", "Warehouse"], ["expense_account", "Account"], ["income_account", "Account"],
|
|
||||||
["buying_cost_center", "Cost Center"], ["selling_cost_center", "Cost Center"]
|
|
||||||
]:
|
|
||||||
if item.get(d[0]):
|
|
||||||
company = frappe.get_value(d[1], item.get(d[0]), "company", cache=True)
|
|
||||||
insert_into_item_defaults(d[0], item.get(d[0]), company)
|
|
||||||
|
|
||||||
doc = frappe.get_doc("Item", item.name)
|
if company not in company_wise_data:
|
||||||
doc.extend("item_defaults", item_defaults)
|
company_wise_data[company] = {}
|
||||||
|
|
||||||
for child_doc in doc.item_defaults:
|
default_data = company_wise_data[company]
|
||||||
child_doc.db_insert()
|
default_data[d[0]] = item_data.get(d[0])
|
||||||
|
|
||||||
|
to_insert_data = []
|
||||||
|
|
||||||
|
# items_default_data data structure will be as follow
|
||||||
|
# {
|
||||||
|
# 'item_code 1': {'company 1': {'default_warehouse': 'Test Warehouse 1'}},
|
||||||
|
# 'item_code 2': {
|
||||||
|
# 'company 1': {'default_warehouse': 'Test Warehouse 1'},
|
||||||
|
# 'company 2': {'default_warehouse': 'Test Warehouse 1'}
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
|
for item_code, companywise_item_data in items_default_data.items():
|
||||||
|
for company, item_default_data in companywise_item_data.items():
|
||||||
|
to_insert_data.append((
|
||||||
|
frappe.generate_hash("", 10),
|
||||||
|
item_code,
|
||||||
|
'Item',
|
||||||
|
'item_defaults',
|
||||||
|
company,
|
||||||
|
item_default_data.get('default_warehouse'),
|
||||||
|
item_default_data.get('expense_account'),
|
||||||
|
item_default_data.get('income_account'),
|
||||||
|
item_default_data.get('buying_cost_center'),
|
||||||
|
item_default_data.get('selling_cost_center'),
|
||||||
|
))
|
||||||
|
|
||||||
|
if to_insert_data:
|
||||||
|
frappe.db.sql('''
|
||||||
|
INSERT INTO `tabItem Default`
|
||||||
|
(
|
||||||
|
`name`, `parent`, `parenttype`, `parentfield`, `company`, `default_warehouse`,
|
||||||
|
`expense_account`, `income_account`, `buying_cost_center`, `selling_cost_center`
|
||||||
|
)
|
||||||
|
VALUES {}
|
||||||
|
'''.format(', '.join(['%s'] * len(to_insert_data))), tuple(to_insert_data))
|
||||||
@@ -103,8 +103,8 @@ class TestTimesheet(unittest.TestCase):
|
|||||||
{
|
{
|
||||||
"billable": 1,
|
"billable": 1,
|
||||||
"activity_type": "_Test Activity Type",
|
"activity_type": "_Test Activity Type",
|
||||||
"from_type": now_datetime(),
|
"from_time": now_datetime(),
|
||||||
"hours": 3,
|
"to_time": now_datetime() + datetime.timedelta(hours=3),
|
||||||
"company": "_Test Company"
|
"company": "_Test Company"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -113,8 +113,8 @@ class TestTimesheet(unittest.TestCase):
|
|||||||
{
|
{
|
||||||
"billable": 1,
|
"billable": 1,
|
||||||
"activity_type": "_Test Activity Type",
|
"activity_type": "_Test Activity Type",
|
||||||
"from_type": now_datetime(),
|
"from_time": now_datetime(),
|
||||||
"hours": 3,
|
"to_time": now_datetime() + datetime.timedelta(hours=3),
|
||||||
"company": "_Test Company"
|
"company": "_Test Company"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -30,23 +30,23 @@ def get_columns():
|
|||||||
"options": "Timesheet",
|
"options": "Timesheet",
|
||||||
"width": 150
|
"width": 150
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": _("Billable Hours"),
|
|
||||||
"fieldtype": "Float",
|
|
||||||
"fieldname": "total_billable_hours",
|
|
||||||
"width": 50
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": _("Working Hours"),
|
"label": _("Working Hours"),
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"fieldname": "total_hours",
|
"fieldname": "total_hours",
|
||||||
"width": 50
|
"width": 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Billable Hours"),
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"fieldname": "total_billable_hours",
|
||||||
|
"width": 150
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": _("Billing Amount"),
|
"label": _("Billing Amount"),
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"fieldname": "amount",
|
"fieldname": "amount",
|
||||||
"width": 100
|
"width": 150
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1290,7 +1290,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if(!r.exc) {
|
if(!r.exc) {
|
||||||
me.frm.set_value("taxes", r.message);
|
for (let tax of r.message) {
|
||||||
|
me.frm.add_child("taxes", tax);
|
||||||
|
}
|
||||||
me.calculate_taxes_and_totals();
|
me.calculate_taxes_and_totals();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -233,30 +233,32 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
|||||||
get_batch_fields: function() {
|
get_batch_fields: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
return [
|
return [
|
||||||
{fieldtype:'Section Break', label: __('Batches')},
|
{ fieldtype: 'Section Break', label: __('Batches') },
|
||||||
{fieldname: 'batches', fieldtype: 'Table',
|
{
|
||||||
|
fieldname: 'batches', fieldtype: 'Table',
|
||||||
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 "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,12 +266,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: {
|
||||||
@@ -292,31 +294,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');
|
||||||
@@ -332,7 +335,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;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
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'
|
||||||
@@ -69,13 +69,13 @@ def get_gl_entries(filters):
|
|||||||
|
|
||||||
gl_entries = frappe.db.sql("""
|
gl_entries = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
gl.posting_date as GlPostDate, gl.name as GlName, gl.account, gl.transaction_date,
|
gl.posting_date as GlPostDate, gl.name as GlName, gl.account, gl.transaction_date,
|
||||||
sum(gl.debit) as debit, sum(gl.credit) as credit,
|
sum(gl.debit) as debit, sum(gl.credit) as credit,
|
||||||
sum(gl.debit_in_account_currency) as debitCurr, sum(gl.credit_in_account_currency) as creditCurr,
|
sum(gl.debit_in_account_currency) as debitCurr, sum(gl.credit_in_account_currency) as creditCurr,
|
||||||
gl.voucher_type, gl.voucher_no, gl.against_voucher_type,
|
gl.voucher_type, gl.voucher_no, gl.against_voucher_type,
|
||||||
gl.against_voucher, gl.account_currency, gl.against,
|
gl.against_voucher, gl.account_currency, gl.against,
|
||||||
gl.party_type, gl.party,
|
gl.party_type, gl.party,
|
||||||
inv.name as InvName, inv.title as InvTitle, inv.posting_date as InvPostDate,
|
inv.name as InvName, inv.title as InvTitle, inv.posting_date as InvPostDate,
|
||||||
pur.name as PurName, pur.title as PurTitle, pur.posting_date as PurPostDate,
|
pur.name as PurName, pur.title as PurTitle, pur.posting_date as PurPostDate,
|
||||||
jnl.cheque_no as JnlRef, jnl.posting_date as JnlPostDate, jnl.title as JnlTitle,
|
jnl.cheque_no as JnlRef, jnl.posting_date as JnlPostDate, jnl.title as JnlTitle,
|
||||||
pay.name as PayName, pay.posting_date as PayPostDate, pay.title as PayTitle,
|
pay.name as PayName, pay.posting_date as PayPostDate, pay.title as PayTitle,
|
||||||
@@ -84,7 +84,7 @@ def get_gl_entries(filters):
|
|||||||
emp.employee_name, emp.name as empName,
|
emp.employee_name, emp.name as empName,
|
||||||
stu.title as student_name, stu.name as stuName,
|
stu.title as student_name, stu.name as stuName,
|
||||||
member_name, mem.name as memName
|
member_name, mem.name as memName
|
||||||
|
|
||||||
from `tabGL Entry` gl
|
from `tabGL Entry` gl
|
||||||
left join `tabSales Invoice` inv on gl.voucher_no = inv.name
|
left join `tabSales Invoice` inv on gl.voucher_no = inv.name
|
||||||
left join `tabPurchase Invoice` pur on gl.voucher_no = pur.name
|
left join `tabPurchase Invoice` pur on gl.voucher_no = pur.name
|
||||||
@@ -124,7 +124,7 @@ def get_result_as_list(data, filters):
|
|||||||
if account_number[0] is not None:
|
if account_number[0] is not None:
|
||||||
CompteNum = account_number[0]
|
CompteNum = account_number[0]
|
||||||
else:
|
else:
|
||||||
frappe.throw(_("Account number for account {0} is not available.<br> Please setup your Chart of Accounts correctly.").format(account.name))
|
frappe.throw(_("Account number for account {0} is not available.<br> Please setup your Chart of Accounts correctly.").format(d.get("account")))
|
||||||
|
|
||||||
if d.get("party_type") == "Customer":
|
if d.get("party_type") == "Customer":
|
||||||
CompAuxNum = d.get("cusName")
|
CompAuxNum = d.get("cusName")
|
||||||
|
|||||||
@@ -29,7 +29,20 @@ frappe.query_reports["HSN-wise-summary of outward supplies"] = {
|
|||||||
"placeholder":"Company GSTIN",
|
"placeholder":"Company GSTIN",
|
||||||
"options": [""],
|
"options": [""],
|
||||||
"width": "80"
|
"width": "80"
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"from_date",
|
||||||
|
"label": __("From Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": "80"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"to_date",
|
||||||
|
"label": __("To Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": "80"
|
||||||
|
},
|
||||||
|
|
||||||
],
|
],
|
||||||
onload: (report) => {
|
onload: (report) => {
|
||||||
fetch_gstins(report);
|
fetch_gstins(report);
|
||||||
|
|||||||
@@ -88,7 +88,9 @@ def get_conditions(filters):
|
|||||||
|
|
||||||
for opts in (("company", " and company=%(company)s"),
|
for opts in (("company", " and company=%(company)s"),
|
||||||
("gst_hsn_code", " and gst_hsn_code=%(gst_hsn_code)s"),
|
("gst_hsn_code", " and gst_hsn_code=%(gst_hsn_code)s"),
|
||||||
("company_gstin", " and company_gstin=%(company_gstin)s")):
|
("company_gstin", " and company_gstin=%(company_gstin)s"),
|
||||||
|
("from_date", " and posting_date >= %(from_date)s"),
|
||||||
|
("to_date", "and posting_date <= %(to_date)s")):
|
||||||
if filters.get(opts[0]):
|
if filters.get(opts[0]):
|
||||||
conditions += opts[1]
|
conditions += opts[1]
|
||||||
|
|
||||||
|
|||||||
@@ -90,22 +90,29 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
|
|||||||
if (this.frm.doc.docstatus===0) {
|
if (this.frm.doc.docstatus===0) {
|
||||||
this.frm.add_custom_button(__('Opportunity'),
|
this.frm.add_custom_button(__('Opportunity'),
|
||||||
function() {
|
function() {
|
||||||
var setters = {};
|
|
||||||
if(me.frm.doc.quotation_to == "Customer" && me.frm.doc.party_name) {
|
|
||||||
setters.customer = me.frm.doc.party_name || undefined;
|
|
||||||
} else if (me.frm.doc.quotation_to == "Lead" && me.frm.doc.party_name) {
|
|
||||||
setters.lead = me.frm.doc.party_name || undefined;
|
|
||||||
}
|
|
||||||
erpnext.utils.map_current_doc({
|
erpnext.utils.map_current_doc({
|
||||||
method: "erpnext.crm.doctype.opportunity.opportunity.make_quotation",
|
method: "erpnext.crm.doctype.opportunity.opportunity.make_quotation",
|
||||||
source_doctype: "Opportunity",
|
source_doctype: "Opportunity",
|
||||||
target: me.frm,
|
target: me.frm,
|
||||||
setters: setters,
|
setters: [
|
||||||
|
{
|
||||||
|
label: "Party",
|
||||||
|
fieldname: "party_name",
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: me.frm.doc.quotation_to,
|
||||||
|
default: me.frm.doc.party_name || undefined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Opportunity Type",
|
||||||
|
fieldname: "opportunity_type",
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Opportunity Type",
|
||||||
|
default: me.frm.doc.order_type || undefined
|
||||||
|
}
|
||||||
|
],
|
||||||
get_query_filters: {
|
get_query_filters: {
|
||||||
status: ["not in", ["Lost", "Closed"]],
|
status: ["not in", ["Lost", "Closed"]],
|
||||||
company: me.frm.doc.company,
|
company: me.frm.doc.company
|
||||||
// cannot set opportunity_type as setter, as the fieldname is order_type
|
|
||||||
opportunity_type: me.frm.doc.order_type,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, __("Get items from"), "btn-default");
|
}, __("Get items from"), "btn-default");
|
||||||
|
|||||||
@@ -161,6 +161,10 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
|
|||||||
"Sales Team": {
|
"Sales Team": {
|
||||||
"doctype": "Sales Team",
|
"doctype": "Sales Team",
|
||||||
"add_if_empty": True
|
"add_if_empty": True
|
||||||
|
},
|
||||||
|
"Payment Schedule": {
|
||||||
|
"doctype": "Payment Schedule",
|
||||||
|
"add_if_empty": True
|
||||||
}
|
}
|
||||||
}, target_doc, set_missing_values, ignore_permissions=ignore_permissions)
|
}, target_doc, set_missing_values, ignore_permissions=ignore_permissions)
|
||||||
|
|
||||||
|
|||||||
@@ -219,13 +219,19 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
method: "erpnext.selling.doctype.quotation.quotation.make_sales_order",
|
method: "erpnext.selling.doctype.quotation.quotation.make_sales_order",
|
||||||
source_doctype: "Quotation",
|
source_doctype: "Quotation",
|
||||||
target: me.frm,
|
target: me.frm,
|
||||||
setters: {
|
setters: [
|
||||||
customer: me.frm.doc.customer || undefined
|
{
|
||||||
},
|
label: "Customer",
|
||||||
|
fieldname: "party_name",
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Customer",
|
||||||
|
default: me.frm.doc.customer || undefined
|
||||||
|
}
|
||||||
|
],
|
||||||
get_query_filters: {
|
get_query_filters: {
|
||||||
company: me.frm.doc.company,
|
company: me.frm.doc.company,
|
||||||
docstatus: 1,
|
docstatus: 1,
|
||||||
status: ["!=", "Lost"],
|
status: ["!=", "Lost"]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, __("Get items from"));
|
}, __("Get items from"));
|
||||||
|
|||||||
@@ -574,8 +574,8 @@ def make_delivery_note(source_name, target_doc=None):
|
|||||||
|
|
||||||
if item:
|
if item:
|
||||||
target.cost_center = frappe.db.get_value("Project", source_parent.project, "cost_center") \
|
target.cost_center = frappe.db.get_value("Project", source_parent.project, "cost_center") \
|
||||||
or item.get("selling_cost_center") \
|
or item.get("buying_cost_center") \
|
||||||
or item_group.get("selling_cost_center")
|
or item_group.get("buying_cost_center")
|
||||||
|
|
||||||
target_doc = get_mapped_doc("Sales Order", source_name, {
|
target_doc = get_mapped_doc("Sales Order", source_name, {
|
||||||
"Sales Order": {
|
"Sales Order": {
|
||||||
|
|||||||
@@ -737,6 +737,17 @@ class POSCart {
|
|||||||
|
|
||||||
const customer = this.frm.doc.customer;
|
const customer = this.frm.doc.customer;
|
||||||
this.customer_field.set_value(customer);
|
this.customer_field.set_value(customer);
|
||||||
|
|
||||||
|
if (this.numpad) {
|
||||||
|
const disable_btns = this.disable_numpad_control()
|
||||||
|
const enable_btns = [__('Rate'), __('Disc')]
|
||||||
|
|
||||||
|
if (disable_btns) {
|
||||||
|
enable_btns.filter(btn => !disable_btns.includes(btn))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.numpad.enable_buttons(enable_btns);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get_grand_total() {
|
get_grand_total() {
|
||||||
@@ -1507,6 +1518,16 @@ class NumberPad {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enable_buttons(btns) {
|
||||||
|
btns.forEach((btn) => {
|
||||||
|
const $btn = this.get_btn(btn);
|
||||||
|
$btn.prop("disabled", false)
|
||||||
|
$btn.hover(() => {
|
||||||
|
$btn.css('cursor','pointer');
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
set_class() {
|
set_class() {
|
||||||
for (const btn in this.add_class) {
|
for (const btn in this.add_class) {
|
||||||
const class_name = this.add_class[btn];
|
const class_name = this.add_class[btn];
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ class DeliveryTrip(Document):
|
|||||||
# Google Maps returns distances in meters by default
|
# Google Maps returns distances in meters by default
|
||||||
self.default_distance_uom = frappe.db.get_single_value("Global Defaults", "default_distance_unit") or "Meter"
|
self.default_distance_uom = frappe.db.get_single_value("Global Defaults", "default_distance_unit") or "Meter"
|
||||||
self.uom_conversion_factor = frappe.db.get_value("UOM Conversion Factor",
|
self.uom_conversion_factor = frappe.db.get_value("UOM Conversion Factor",
|
||||||
{"from_uom": "Meter", "to_uom": self.default_distance_uom},
|
{"from_uom": "Meter", "to_uom": self.default_distance_uom}, "value")
|
||||||
"value")
|
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_stop_addresses()
|
self.validate_stop_addresses()
|
||||||
@@ -139,7 +138,7 @@ class DeliveryTrip(Document):
|
|||||||
# Include last leg in the final distance calculation
|
# Include last leg in the final distance calculation
|
||||||
self.uom = self.default_distance_uom
|
self.uom = self.default_distance_uom
|
||||||
total_distance = sum([leg.get("distance", {}).get("value", 0.0)
|
total_distance = sum([leg.get("distance", {}).get("value", 0.0)
|
||||||
for leg in directions.get("legs")]) # in meters
|
for leg in directions.get("legs")]) # in meters
|
||||||
self.total_distance = total_distance * self.uom_conversion_factor
|
self.total_distance = total_distance * self.uom_conversion_factor
|
||||||
else:
|
else:
|
||||||
idx += len(route) - 1
|
idx += len(route) - 1
|
||||||
@@ -358,8 +357,12 @@ def notify_customers(delivery_trip):
|
|||||||
email_recipients = []
|
email_recipients = []
|
||||||
|
|
||||||
for stop in delivery_trip.delivery_stops:
|
for stop in delivery_trip.delivery_stops:
|
||||||
contact_info = frappe.db.get_value("Contact", stop.contact,
|
contact_info = frappe.db.get_value("Contact", stop.contact, ["first_name", "last_name", "email_id"], as_dict=1)
|
||||||
["first_name", "last_name", "email_id", "gender"], as_dict=1)
|
|
||||||
|
context.update({"items": []})
|
||||||
|
if stop.delivery_note:
|
||||||
|
items = frappe.get_all("Delivery Note Item", filters={"parent": stop.delivery_note, "docstatus": 1}, fields=["*"])
|
||||||
|
context.update({"items": items})
|
||||||
|
|
||||||
if contact_info and contact_info.email_id:
|
if contact_info and contact_info.email_id:
|
||||||
context.update(stop.as_dict())
|
context.update(stop.as_dict())
|
||||||
@@ -369,9 +372,9 @@ def notify_customers(delivery_trip):
|
|||||||
dispatch_template = frappe.get_doc("Email Template", dispatch_template_name)
|
dispatch_template = frappe.get_doc("Email Template", dispatch_template_name)
|
||||||
|
|
||||||
frappe.sendmail(recipients=contact_info.email_id,
|
frappe.sendmail(recipients=contact_info.email_id,
|
||||||
subject=dispatch_template.subject,
|
subject=dispatch_template.subject,
|
||||||
message=frappe.render_template(dispatch_template.response, context),
|
message=frappe.render_template(dispatch_template.response, context),
|
||||||
attachments=get_attachments(stop))
|
attachments=get_attachments(stop))
|
||||||
|
|
||||||
stop.db_set("email_sent_to", contact_info.email_id)
|
stop.db_set("email_sent_to", contact_info.email_id)
|
||||||
email_recipients.append(contact_info.email_id)
|
email_recipients.append(contact_info.email_id)
|
||||||
@@ -388,9 +391,7 @@ def get_attachments(delivery_stop):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
dispatch_attachment = frappe.db.get_single_value("Delivery Settings", "dispatch_attachment")
|
dispatch_attachment = frappe.db.get_single_value("Delivery Settings", "dispatch_attachment")
|
||||||
attachments = frappe.attach_print("Delivery Note",
|
attachments = frappe.attach_print("Delivery Note", delivery_stop.delivery_note,
|
||||||
delivery_stop.delivery_note,
|
file_name="Delivery Note", print_format=dispatch_attachment)
|
||||||
file_name="Delivery Note",
|
|
||||||
print_format=dispatch_attachment)
|
|
||||||
|
|
||||||
return [attachments]
|
return [attachments]
|
||||||
|
|||||||
@@ -67,8 +67,6 @@ class Item(WebsiteGenerator):
|
|||||||
from frappe.model.naming import set_name_by_naming_series
|
from frappe.model.naming import set_name_by_naming_series
|
||||||
set_name_by_naming_series(self)
|
set_name_by_naming_series(self)
|
||||||
self.item_code = self.name
|
self.item_code = self.name
|
||||||
elif not self.item_code:
|
|
||||||
msgprint(_("Item Code is mandatory because Item is not automatically numbered"), raise_exception=1)
|
|
||||||
|
|
||||||
self.item_code = strip(self.item_code)
|
self.item_code = strip(self.item_code)
|
||||||
self.name = self.item_code
|
self.name = self.item_code
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ frappe.ui.form.on('Material Request', {
|
|||||||
var values = d.get_values();
|
var values = d.get_values();
|
||||||
if(!values) return;
|
if(!values) return;
|
||||||
values["company"] = frm.doc.company;
|
values["company"] = frm.doc.company;
|
||||||
|
if(!frm.doc.company) frappe.throw(__("Company field is required"));
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.manufacturing.doctype.bom.bom.get_bom_items",
|
method: "erpnext.manufacturing.doctype.bom.bom.get_bom_items",
|
||||||
args: values,
|
args: values,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from frappe.utils import flt, cint, nowdate
|
|||||||
|
|
||||||
from frappe import throw, _
|
from frappe import throw, _
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate, cint
|
||||||
from erpnext.controllers.buying_controller import BuyingController
|
from erpnext.controllers.buying_controller import BuyingController
|
||||||
from erpnext.accounts.utils import get_account_currency
|
from erpnext.accounts.utils import get_account_currency
|
||||||
from frappe.desk.notifications import clear_doctype_notifications
|
from frappe.desk.notifications import clear_doctype_notifications
|
||||||
@@ -128,7 +128,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
self.company, self.base_grand_total)
|
self.company, self.base_grand_total)
|
||||||
|
|
||||||
self.update_prevdoc_status()
|
self.update_prevdoc_status()
|
||||||
if self.per_billed < 100:
|
if cint(self.per_billed) < 100:
|
||||||
self.update_billing_status()
|
self.update_billing_status()
|
||||||
else:
|
else:
|
||||||
self.status = "Completed"
|
self.status = "Completed"
|
||||||
|
|||||||
@@ -562,7 +562,7 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
for d in stock_entry.get("items"):
|
for d in stock_entry.get("items"):
|
||||||
if d.item_code != "_Test FG Item 2":
|
if d.item_code != "_Test FG Item 2":
|
||||||
rm_cost += flt(d.amount)
|
rm_cost += flt(d.amount)
|
||||||
fg_cost = filter(lambda x: x.item_code=="_Test FG Item 2", stock_entry.get("items"))[0].amount
|
fg_cost = list(filter(lambda x: x.item_code=="_Test FG Item 2", stock_entry.get("items")))[0].amount
|
||||||
self.assertEqual(fg_cost,
|
self.assertEqual(fg_cost,
|
||||||
flt(rm_cost + bom_operation_cost + work_order.additional_operating_cost, 2))
|
flt(rm_cost + bom_operation_cost + work_order.additional_operating_cost, 2))
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,23 @@ frappe.query_reports["Total Stock Summary"] = {
|
|||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"width": "80",
|
"width": "80",
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"options": ["","Warehouse", "Company"],
|
"options": ["", "Warehouse", "Company"],
|
||||||
"default": "Warehouse"
|
"change": function() {
|
||||||
|
let group_by = frappe.query_report.get_filter_value("group_by")
|
||||||
|
let company_filter = frappe.query_report.get_filter("company")
|
||||||
|
if (group_by == "Company") {
|
||||||
|
company_filter.df.reqd = 0;
|
||||||
|
company_filter.df.hidden = 1;
|
||||||
|
frappe.query_report.set_filter_value("company", "");
|
||||||
|
company_filter.refresh();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
company_filter.df.reqd = 1;
|
||||||
|
company_filter.df.hidden = 0;
|
||||||
|
company_filter.refresh();
|
||||||
|
frappe.query_report.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
@@ -23,4 +38,4 @@ frappe.query_reports["Total Stock Summary"] = {
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ from jinja2 import utils
|
|||||||
from html2text import html2text
|
from html2text import html2text
|
||||||
from frappe.utils import sanitize_html
|
from frappe.utils import sanitize_html
|
||||||
from frappe.utils.global_search import search
|
from frappe.utils.global_search import search
|
||||||
|
from six import text_type
|
||||||
|
|
||||||
def get_context(context):
|
def get_context(context):
|
||||||
context.no_cache = 1
|
context.no_cache = 1
|
||||||
@@ -12,7 +13,7 @@ def get_context(context):
|
|||||||
query = str(utils.escape(sanitize_html(frappe.form_dict.q)))
|
query = str(utils.escape(sanitize_html(frappe.form_dict.q)))
|
||||||
context.title = _('Help Results for')
|
context.title = _('Help Results for')
|
||||||
context.query = query
|
context.query = query
|
||||||
|
|
||||||
context.route = '/search_help'
|
context.route = '/search_help'
|
||||||
d = frappe._dict()
|
d = frappe._dict()
|
||||||
d.results_sections = get_help_results_sections(query)
|
d.results_sections = get_help_results_sections(query)
|
||||||
@@ -73,7 +74,7 @@ def prepare_api_results(api, topics_data):
|
|||||||
for topic in topics_data:
|
for topic in topics_data:
|
||||||
route = api.base_url + '/' + (api.post_route + '/' if api.post_route else "")
|
route = api.base_url + '/' + (api.post_route + '/' if api.post_route else "")
|
||||||
for key in api.post_route_key_list.split(','):
|
for key in api.post_route_key_list.split(','):
|
||||||
route += unicode(topic[key])
|
route += text_type(topic[key])
|
||||||
|
|
||||||
results.append(frappe._dict({
|
results.append(frappe._dict({
|
||||||
'title': topic[api.post_title_key],
|
'title': topic[api.post_title_key],
|
||||||
|
|||||||
12
travis/run-tests.sh
Executable file
12
travis/run-tests.sh
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [[ $TEST_TYPE == 'Server Side Test' ]]; then
|
||||||
|
bench run-tests --app erpnext --coverage
|
||||||
|
|
||||||
|
elif [[ $TEST_TYPE == 'Patch Test' ]]; then
|
||||||
|
wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz
|
||||||
|
bench --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz --mariadb-root-password travis
|
||||||
|
bench migrate
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user