Merge branch 'develop' into not_able_to_save_asset_for_manual_method_develop
This commit is contained in:
commit
2f4f9a2af0
25
.travis.yml
25
.travis.yml
@ -3,6 +3,11 @@ dist: trusty
|
||||
|
||||
python:
|
||||
- "2.7"
|
||||
- "3.6"
|
||||
|
||||
env:
|
||||
- TEST_TYPE="Server Side Test"
|
||||
- TEST_TYPE="Patch Test"
|
||||
|
||||
services:
|
||||
- mysql
|
||||
@ -39,18 +44,8 @@ before_script:
|
||||
- bench start &
|
||||
- sleep 10
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: test
|
||||
script:
|
||||
- set -e
|
||||
- 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
|
||||
script:
|
||||
- bash $TRAVIS_BUILD_DIR/travis/run-tests.sh
|
||||
|
||||
after_script:
|
||||
- coveralls -b apps/erpnext -d ../../sites/.coverage
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe, json
|
||||
from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day
|
||||
from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day, formatdate
|
||||
from erpnext.accounts.report.general_ledger.general_ledger import execute
|
||||
from frappe.core.page.dashboard.dashboard import cache_source, get_from_date_from_timespan
|
||||
from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_period_ending
|
||||
@ -37,7 +37,7 @@ def get(chart_name=None, from_date = None, to_date = None):
|
||||
result = build_result(account, dates, gl_entries)
|
||||
|
||||
return {
|
||||
"labels": [r[0].strftime('%Y-%m-%d') for r in result],
|
||||
"labels": [formatdate(r[0].strftime('%Y-%m-%d')) for r in result],
|
||||
"datasets": [{
|
||||
"name": account,
|
||||
"values": [r[1] for r in result]
|
||||
|
@ -43,8 +43,13 @@ frappe.ui.form.on('Bank Guarantee', {
|
||||
|
||||
reference_docname: function(frm) {
|
||||
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";
|
||||
|
||||
if (frm.doc.reference_doctype == "Sales Order") {
|
||||
fields_to_fetch.push("project");
|
||||
}
|
||||
|
||||
fields_to_fetch.push(party_field);
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.bank_guarantee.bank_guarantee.get_vouchar_detials",
|
||||
|
@ -232,7 +232,7 @@ def filter_pricing_rules(args, pricing_rules, doc=None):
|
||||
if len(pricing_rules) > 1:
|
||||
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":
|
||||
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
|
||||
|
||||
if len(pricing_rules) > 1 and not args.for_shopping_cart:
|
||||
|
@ -390,8 +390,8 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters):
|
||||
if filters:
|
||||
if filters.get("project"):
|
||||
if not isinstance(filters.get("project"), list):
|
||||
projects = frappe.safe_encode(filters.get("project"))
|
||||
filters.project = [d.strip() for d in projects.strip().split(',') if d]
|
||||
filters.project = frappe.parse_json(filters.get("project"))
|
||||
|
||||
additional_conditions.append("project in %(project)s")
|
||||
|
||||
if filters.get("cost_center"):
|
||||
|
@ -72,46 +72,25 @@ frappe.query_reports["General Ledger"] = {
|
||||
{
|
||||
"fieldname":"party",
|
||||
"label": __("Party"),
|
||||
"fieldtype": "MultiSelect",
|
||||
get_data: function() {
|
||||
"fieldtype": "MultiSelectList",
|
||||
get_data: function(txt) {
|
||||
if (!frappe.query_report.filters) return;
|
||||
var party_type = frappe.query_report.get_filter_value('party_type');
|
||||
var parties = frappe.query_report.get_filter_value('party');
|
||||
if(!party_type) return;
|
||||
|
||||
const values = parties.split(/\s*,\s*/).filter(d => d);
|
||||
const txt = parties.match(/[^,\s*]*$/)[0] || '';
|
||||
let data = [];
|
||||
let party_type = frappe.query_report.get_filter_value('party_type');
|
||||
if (!party_type) return;
|
||||
|
||||
frappe.call({
|
||||
type: "GET",
|
||||
method:'frappe.desk.search.search_link',
|
||||
async: false,
|
||||
no_spinner: true,
|
||||
args: {
|
||||
doctype: frappe.query_report.get_filter_value('party_type'),
|
||||
txt: txt,
|
||||
filters: {
|
||||
"name": ["not in", values]
|
||||
}
|
||||
},
|
||||
callback: function(r) {
|
||||
data = r.results;
|
||||
}
|
||||
});
|
||||
return data;
|
||||
return frappe.db.get_link_options(party_type, txt);
|
||||
},
|
||||
on_change: function() {
|
||||
var party_type = frappe.query_report.get_filter_value('party_type');
|
||||
var parties = frappe.query_report.get_filter_value('party');
|
||||
const values = parties.split(/\s*,\s*/).filter(d => d);
|
||||
|
||||
if(!party_type || !parties || values.length>1) {
|
||||
if(!party_type || parties.length === 0 || parties.length > 1) {
|
||||
frappe.query_report.set_filter_value('party_name', "");
|
||||
frappe.query_report.set_filter_value('tax_id', "");
|
||||
return;
|
||||
} else {
|
||||
var party = values[0];
|
||||
var party = parties[0];
|
||||
var fieldname = erpnext.utils.get_party_name(party_type) || "name";
|
||||
frappe.db.get_value(party_type, party, fieldname, function(value) {
|
||||
frappe.query_report.set_filter_value('party_name', value[fieldname]);
|
||||
@ -154,62 +133,17 @@ frappe.query_reports["General Ledger"] = {
|
||||
{
|
||||
"fieldname":"cost_center",
|
||||
"label": __("Cost Center"),
|
||||
"fieldtype": "MultiSelect",
|
||||
get_data: function() {
|
||||
var cost_centers = frappe.query_report.get_filter_value("cost_center") || "";
|
||||
|
||||
const values = cost_centers.split(/\s*,\s*/).filter(d => d);
|
||||
const txt = cost_centers.match(/[^,\s*]*$/)[0] || '';
|
||||
let data = [];
|
||||
|
||||
frappe.call({
|
||||
type: "GET",
|
||||
method:'frappe.desk.search.search_link',
|
||||
async: false,
|
||||
no_spinner: true,
|
||||
args: {
|
||||
doctype: "Cost Center",
|
||||
txt: txt,
|
||||
filters: {
|
||||
"company": frappe.query_report.get_filter_value("company"),
|
||||
"name": ["not in", values]
|
||||
}
|
||||
},
|
||||
callback: function(r) {
|
||||
data = r.results;
|
||||
}
|
||||
});
|
||||
return data;
|
||||
"fieldtype": "MultiSelectList",
|
||||
get_data: function(txt) {
|
||||
return frappe.db.get_link_options('Cost Center', txt);
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"project",
|
||||
"label": __("Project"),
|
||||
"fieldtype": "MultiSelect",
|
||||
get_data: function() {
|
||||
var projects = frappe.query_report.get_filter_value("project") || "";
|
||||
|
||||
const values = projects.split(/\s*,\s*/).filter(d => d);
|
||||
const txt = projects.match(/[^,\s*]*$/)[0] || '';
|
||||
let data = [];
|
||||
|
||||
frappe.call({
|
||||
type: "GET",
|
||||
method:'frappe.desk.search.search_link',
|
||||
async: false,
|
||||
no_spinner: true,
|
||||
args: {
|
||||
doctype: "Project",
|
||||
txt: txt,
|
||||
filters: {
|
||||
"name": ["not in", values]
|
||||
}
|
||||
},
|
||||
callback: function(r) {
|
||||
data = r.results;
|
||||
}
|
||||
});
|
||||
return data;
|
||||
"fieldtype": "MultiSelectList",
|
||||
get_data: function(txt) {
|
||||
return frappe.db.get_link_options('Project', txt);
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -26,8 +26,7 @@ def execute(filters=None):
|
||||
account_details.setdefault(acc.name, acc)
|
||||
|
||||
if filters.get('party'):
|
||||
parties = cstr(filters.get("party")).strip()
|
||||
filters.party = [d.strip() for d in parties.split(',') if d]
|
||||
filters.party = frappe.parse_json(filters.get("party"))
|
||||
|
||||
validate_filters(filters, account_details)
|
||||
|
||||
@ -61,12 +60,10 @@ def validate_filters(filters, account_details):
|
||||
frappe.throw(_("From Date must be before To Date"))
|
||||
|
||||
if filters.get('project'):
|
||||
projects = cstr(filters.get("project")).strip()
|
||||
filters.project = [d.strip() for d in projects.split(',') if d]
|
||||
filters.project = frappe.parse_json(filters.get('project'))
|
||||
|
||||
if filters.get('cost_center'):
|
||||
cost_centers = cstr(filters.get("cost_center")).strip()
|
||||
filters.cost_center = [d.strip() for d in cost_centers.split(',') if d]
|
||||
filters.cost_center = frappe.parse_json(filters.get('cost_center'))
|
||||
|
||||
|
||||
def validate_party(filters):
|
||||
|
@ -13,33 +13,11 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
|
||||
frappe.query_reports["Gross and Net Profit Report"]["filters"].push(
|
||||
{
|
||||
"fieldname":"project",
|
||||
"fieldname": "project",
|
||||
"label": __("Project"),
|
||||
"fieldtype": "MultiSelect",
|
||||
get_data: function() {
|
||||
var projects = frappe.query_report.get_filter_value("project") || "";
|
||||
|
||||
const values = projects.split(/\s*,\s*/).filter(d => d);
|
||||
const txt = projects.match(/[^,\s*]*$/)[0] || '';
|
||||
let data = [];
|
||||
|
||||
frappe.call({
|
||||
type: "GET",
|
||||
method:'frappe.desk.search.search_link',
|
||||
async: false,
|
||||
no_spinner: true,
|
||||
args: {
|
||||
doctype: "Project",
|
||||
txt: txt,
|
||||
filters: {
|
||||
"name": ["not in", values]
|
||||
}
|
||||
},
|
||||
callback: function(r) {
|
||||
data = r.results;
|
||||
}
|
||||
});
|
||||
return data;
|
||||
"fieldtype": "MultiSelectList",
|
||||
get_data: function(txt) {
|
||||
return frappe.db.get_link_options('Project', txt);
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -8,33 +8,11 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
|
||||
frappe.query_reports["Profit and Loss Statement"]["filters"].push(
|
||||
{
|
||||
"fieldname":"project",
|
||||
"fieldname": "project",
|
||||
"label": __("Project"),
|
||||
"fieldtype": "MultiSelect",
|
||||
get_data: function() {
|
||||
var projects = frappe.query_report.get_filter_value("project") || "";
|
||||
|
||||
const values = projects.split(/\s*,\s*/).filter(d => d);
|
||||
const txt = projects.match(/[^,\s*]*$/)[0] || '';
|
||||
let data = [];
|
||||
|
||||
frappe.call({
|
||||
type: "GET",
|
||||
method:'frappe.desk.search.search_link',
|
||||
async: false,
|
||||
no_spinner: true,
|
||||
args: {
|
||||
doctype: "Project",
|
||||
txt: txt,
|
||||
filters: {
|
||||
"name": ["not in", values]
|
||||
}
|
||||
},
|
||||
callback: function(r) {
|
||||
data = r.results;
|
||||
}
|
||||
});
|
||||
return data;
|
||||
"fieldtype": "MultiSelectList",
|
||||
get_data: function(txt) {
|
||||
return frappe.db.get_link_options('Project', txt);
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -25,9 +25,12 @@ class TestLocation(unittest.TestCase):
|
||||
temp['features'][0]['properties']['feature_of'] = location
|
||||
formatted_locations.extend(temp['features'])
|
||||
|
||||
formatted_location_string = str(formatted_locations)
|
||||
test_location = frappe.get_doc('Location', 'Test Location Area')
|
||||
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'))
|
||||
|
@ -342,7 +342,7 @@ def assign_to_user(doc, subject_field):
|
||||
elif doc.lead:
|
||||
assign_user = frappe.db.get_value('Lead', doc.lead, 'lead_owner')
|
||||
|
||||
if assign_user and assign_user != 'Administrator':
|
||||
if assign_user and assign_user not in ['Administrator', 'Guest']:
|
||||
if not assign_to.get(dict(doctype = doc.doctype, name = doc.name)):
|
||||
assign_to.add({
|
||||
"assign_to": assign_user,
|
||||
|
@ -97,7 +97,7 @@ def get_expenses():
|
||||
"expense_date": frappe.flags.current_date,
|
||||
"expense_type": expense_type.name,
|
||||
"default_account": expense_type.default_account or "Miscellaneous Expenses - WPL",
|
||||
"claim_amount": claim_amount,
|
||||
"amount": claim_amount,
|
||||
"sanctioned_amount": claim_amount
|
||||
})
|
||||
|
||||
@ -107,7 +107,7 @@ def update_sanctioned_amount(expense_claim):
|
||||
for expense in expense_claim.expenses:
|
||||
sanctioned_amount = random.randint(1,20)*10
|
||||
|
||||
if sanctioned_amount < expense.claim_amount:
|
||||
if sanctioned_amount < expense.amount:
|
||||
expense.sanctioned_amount = sanctioned_amount
|
||||
|
||||
def get_timesheet_based_salary_slip_employee():
|
||||
|
@ -10,6 +10,7 @@ app_color = "#e74c3c"
|
||||
app_email = "info@erpnext.com"
|
||||
app_license = "GNU General Public License (v3)"
|
||||
source_link = "https://github.com/frappe/erpnext"
|
||||
app_logo_url = '/assets/erpnext/images/erp-icon.svg'
|
||||
|
||||
|
||||
develop_version = '12.x.x-develop'
|
||||
|
@ -2,9 +2,8 @@ frappe.ui.form.on("Employee Attendance Tool", {
|
||||
refresh: function(frm) {
|
||||
frm.disable_save();
|
||||
},
|
||||
|
||||
|
||||
onload: function(frm) {
|
||||
frm.doc.department = frm.doc.branch = frm.doc.company = "All";
|
||||
frm.set_value("date", frappe.datetime.get_today());
|
||||
erpnext.employee_attendance_tool.load_employees(frm);
|
||||
},
|
||||
@ -24,7 +23,7 @@ frappe.ui.form.on("Employee Attendance Tool", {
|
||||
company: function(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_marked = []
|
||||
filters = {"status": "Active", "date_of_joining": ["<=", date]}
|
||||
if department != "All":
|
||||
filters["department"] = department
|
||||
if branch != "All":
|
||||
filters["branch"] = branch
|
||||
if company != "All":
|
||||
filters["company"] = company
|
||||
|
||||
for field, value in {'department': department,
|
||||
'branch': branch, 'company': company}.items():
|
||||
if value:
|
||||
filters[field] = value
|
||||
|
||||
employee_list = frappe.get_list("Employee", fields=["employee", "employee_name"], filters=filters, order_by="employee_name")
|
||||
marked_employee = {}
|
||||
|
@ -2,20 +2,8 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
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) {
|
||||
frm.trigger('set_earning_component');
|
||||
var method, args;
|
||||
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";
|
||||
@ -35,6 +23,21 @@ frappe.ui.form.on('Employee Benefit Application', {
|
||||
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) {
|
||||
var method, args;
|
||||
if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
|
||||
|
@ -109,12 +109,9 @@ cur_frm.cscript.calculate_total = function(doc){
|
||||
doc.total_claimed_amount = 0;
|
||||
doc.total_sanctioned_amount = 0;
|
||||
$.each((doc.expenses || []), function(i, d) {
|
||||
doc.total_claimed_amount += d.claim_amount;
|
||||
doc.total_claimed_amount += d.amount;
|
||||
doc.total_sanctioned_amount += d.sanctioned_amount;
|
||||
});
|
||||
|
||||
refresh_field("total_claimed_amount");
|
||||
refresh_field('total_sanctioned_amount');
|
||||
};
|
||||
|
||||
cur_frm.cscript.calculate_total_amount = function(doc,cdt,cdn){
|
||||
@ -157,6 +154,14 @@ frappe.ui.form.on("Expense Claim", {
|
||||
}
|
||||
};
|
||||
});
|
||||
frm.set_query("account_head", "taxes", function(doc) {
|
||||
return {
|
||||
filters: [
|
||||
['company', '=', doc.company],
|
||||
['account_type', 'in', ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation"]]
|
||||
]
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
onload: function(frm) {
|
||||
@ -197,6 +202,12 @@ frappe.ui.form.on("Expense Claim", {
|
||||
}
|
||||
},
|
||||
|
||||
calculate_grand_total: function(frm) {
|
||||
var grand_total = flt(frm.doc.total_sanctioned_amount) + flt(frm.doc.total_taxes_and_charges) - flt(frm.doc.total_advance_amount);
|
||||
frm.set_value("grand_total", grand_total);
|
||||
frm.refresh_fields();
|
||||
},
|
||||
|
||||
make_payment_entry: function(frm) {
|
||||
var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry";
|
||||
if(frm.doc.__onload && frm.doc.__onload.make_payment_via_journal_entry) {
|
||||
@ -259,6 +270,18 @@ frappe.ui.form.on("Expense Claim", {
|
||||
frm.events.get_advances(frm);
|
||||
},
|
||||
|
||||
get_taxes: function(frm) {
|
||||
if(frm.doc.taxes) {
|
||||
frappe.call({
|
||||
method: "calculate_taxes",
|
||||
doc: frm.doc,
|
||||
callback: () => {
|
||||
refresh_field("taxes");
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
get_advances: function(frm) {
|
||||
frappe.model.clear_table(frm.doc, "advances");
|
||||
if (frm.doc.employee) {
|
||||
@ -288,16 +311,18 @@ frappe.ui.form.on("Expense Claim", {
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Expense Claim Detail", {
|
||||
claim_amount: function(frm, cdt, cdn) {
|
||||
amount: function(frm, cdt, cdn) {
|
||||
var child = locals[cdt][cdn];
|
||||
var doc = frm.doc;
|
||||
frappe.model.set_value(cdt, cdn, 'sanctioned_amount', child.claim_amount);
|
||||
frappe.model.set_value(cdt, cdn, 'sanctioned_amount', child.amount);
|
||||
cur_frm.cscript.calculate_total(doc,cdt,cdn);
|
||||
},
|
||||
|
||||
sanctioned_amount: function(frm, cdt, cdn) {
|
||||
var doc = frm.doc;
|
||||
cur_frm.cscript.calculate_total(doc,cdt,cdn);
|
||||
frm.trigger("get_taxes");
|
||||
frm.trigger("calculate_grand_total");
|
||||
}
|
||||
});
|
||||
|
||||
@ -324,6 +349,7 @@ frappe.ui.form.on("Expense Claim Advance", {
|
||||
child.advance_paid = r.message[0].paid_amount;
|
||||
child.unclaimed_amount = flt(r.message[0].paid_amount) - flt(r.message[0].claimed_amount);
|
||||
child.allocated_amount = flt(r.message[0].paid_amount) - flt(r.message[0].claimed_amount);
|
||||
frm.trigger('calculate_grand_total');
|
||||
refresh_field("advances");
|
||||
}
|
||||
}
|
||||
@ -332,6 +358,43 @@ frappe.ui.form.on("Expense Claim Advance", {
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Expense Taxes and Charges", {
|
||||
account_head: function(frm, cdt, cdn) {
|
||||
var child = locals[cdt][cdn];
|
||||
if(child.account_head && !child.description) {
|
||||
// set description from account head
|
||||
child.description = child.account_head.split(' - ').slice(0, -1).join(' - ');
|
||||
refresh_field("taxes");
|
||||
}
|
||||
},
|
||||
|
||||
calculate_total_tax: function(frm, cdt, cdn) {
|
||||
var child = locals[cdt][cdn];
|
||||
child.total = flt(frm.doc.total_sanctioned_amount) + flt(child.tax_amount);
|
||||
frm.trigger("calculate_tax_amount", cdt, cdn);
|
||||
},
|
||||
|
||||
calculate_tax_amount: function(frm) {
|
||||
frm.doc.total_taxes_and_charges = 0;
|
||||
(frm.doc.taxes || []).forEach(function(d) {
|
||||
frm.doc.total_taxes_and_charges += d.tax_amount;
|
||||
});
|
||||
frm.trigger("calculate_grand_total");
|
||||
},
|
||||
|
||||
rate: function(frm, cdt, cdn) {
|
||||
var child = locals[cdt][cdn];
|
||||
if(!child.amount) {
|
||||
child.tax_amount = flt(frm.doc.total_sanctioned_amount) * (flt(child.rate)/100);
|
||||
}
|
||||
frm.trigger("calculate_total_tax", cdt, cdn);
|
||||
},
|
||||
|
||||
tax_amount: function(frm, cdt, cdn) {
|
||||
frm.trigger("calculate_total_tax", cdt, cdn);
|
||||
}
|
||||
});
|
||||
|
||||
cur_frm.fields_dict['task'].get_query = function(doc) {
|
||||
return {
|
||||
filters:{
|
||||
|
@ -4,6 +4,7 @@
|
||||
"creation": "2013-01-10 16:34:14",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"naming_series",
|
||||
"employee",
|
||||
@ -12,17 +13,24 @@
|
||||
"column_break_5",
|
||||
"expense_approver",
|
||||
"approval_status",
|
||||
"total_claimed_amount",
|
||||
"total_sanctioned_amount",
|
||||
"is_paid",
|
||||
"expense_details",
|
||||
"expenses",
|
||||
"sb1",
|
||||
"taxes",
|
||||
"transactions_section",
|
||||
"total_sanctioned_amount",
|
||||
"total_taxes_and_charges",
|
||||
"total_advance_amount",
|
||||
"column_break_17",
|
||||
"grand_total",
|
||||
"total_claimed_amount",
|
||||
"total_amount_reimbursed",
|
||||
"section_break_16",
|
||||
"posting_date",
|
||||
"vehicle_log",
|
||||
"task",
|
||||
"cb1",
|
||||
"total_amount_reimbursed",
|
||||
"remark",
|
||||
"title",
|
||||
"email_id",
|
||||
@ -39,8 +47,7 @@
|
||||
"status",
|
||||
"amended_from",
|
||||
"advance_payments",
|
||||
"advances",
|
||||
"total_advance_amount"
|
||||
"advances"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@ -117,7 +124,6 @@
|
||||
{
|
||||
"fieldname": "total_sanctioned_amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Total Sanctioned Amount",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "total_sanctioned_amount",
|
||||
@ -315,12 +321,45 @@
|
||||
{
|
||||
"fieldname": "dimension_col_break",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "taxes",
|
||||
"fieldtype": "Table",
|
||||
"label": "Expense Taxes and Charges",
|
||||
"options": "Expense Taxes and Charges"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_16",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "transactions_section",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "grand_total",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Grand Total",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_17",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "total_taxes_and_charges",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Taxes and Charges",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-money",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"modified": "2019-05-25 22:53:31.682151",
|
||||
"modified": "2019-06-13 18:05:52.530462",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Expense Claim",
|
||||
|
@ -12,6 +12,7 @@ from erpnext.accounts.general_ledger import make_gl_entries
|
||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
from frappe.utils.csvutils import getlink
|
||||
from erpnext.accounts.utils import get_account_currency
|
||||
|
||||
class InvalidExpenseApproverError(frappe.ValidationError): pass
|
||||
class ExpenseApproverIdentityError(frappe.ValidationError): pass
|
||||
@ -29,6 +30,7 @@ class ExpenseClaim(AccountsController):
|
||||
self.set_expense_account(validate=True)
|
||||
self.set_payable_account()
|
||||
self.set_cost_center()
|
||||
self.calculate_taxes()
|
||||
self.set_status()
|
||||
if self.task and not self.project:
|
||||
self.project = frappe.db.get_value("Task", self.task, "project")
|
||||
@ -93,7 +95,7 @@ class ExpenseClaim(AccountsController):
|
||||
elif self.project:
|
||||
frappe.get_doc("Project", self.project).update_project()
|
||||
|
||||
def make_gl_entries(self, cancel = False):
|
||||
def make_gl_entries(self, cancel=False):
|
||||
if flt(self.total_sanctioned_amount) > 0:
|
||||
gl_entries = self.get_gl_entries()
|
||||
make_gl_entries(gl_entries, cancel)
|
||||
@ -102,15 +104,13 @@ class ExpenseClaim(AccountsController):
|
||||
gl_entry = []
|
||||
self.validate_account_details()
|
||||
|
||||
payable_amount = flt(self.total_sanctioned_amount) - flt(self.total_advance_amount)
|
||||
|
||||
# payable entry
|
||||
if payable_amount:
|
||||
if self.grand_total:
|
||||
gl_entry.append(
|
||||
self.get_gl_dict({
|
||||
"account": self.payable_account,
|
||||
"credit": payable_amount,
|
||||
"credit_in_account_currency": payable_amount,
|
||||
"credit": self.grand_total,
|
||||
"credit_in_account_currency": self.grand_total,
|
||||
"against": ",".join([d.default_account for d in self.expenses]),
|
||||
"party_type": "Employee",
|
||||
"party": self.employee,
|
||||
@ -144,15 +144,16 @@ class ExpenseClaim(AccountsController):
|
||||
"against_voucher": self.name
|
||||
})
|
||||
)
|
||||
self.add_tax_gl_entries(gl_entry)
|
||||
|
||||
if self.is_paid and payable_amount:
|
||||
if self.is_paid and self.grand_total:
|
||||
# payment entry
|
||||
payment_account = get_bank_cash_account(self.mode_of_payment, self.company).get("account")
|
||||
gl_entry.append(
|
||||
self.get_gl_dict({
|
||||
"account": payment_account,
|
||||
"credit": payable_amount,
|
||||
"credit_in_account_currency": payable_amount,
|
||||
"credit": self.grand_total,
|
||||
"credit_in_account_currency": self.grand_total,
|
||||
"against": self.employee
|
||||
})
|
||||
)
|
||||
@ -163,8 +164,8 @@ class ExpenseClaim(AccountsController):
|
||||
"party_type": "Employee",
|
||||
"party": self.employee,
|
||||
"against": payment_account,
|
||||
"debit": payable_amount,
|
||||
"debit_in_account_currency": payable_amount,
|
||||
"debit": self.grand_total,
|
||||
"debit_in_account_currency": self.grand_total,
|
||||
"against_voucher": self.name,
|
||||
"against_voucher_type": self.doctype,
|
||||
})
|
||||
@ -172,6 +173,21 @@ class ExpenseClaim(AccountsController):
|
||||
|
||||
return gl_entry
|
||||
|
||||
def add_tax_gl_entries(self, gl_entries):
|
||||
# tax table gl entries
|
||||
for tax in self.get("taxes"):
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": tax.account_head,
|
||||
"debit": tax.tax_amount,
|
||||
"debit_in_account_currency": tax.tax_amount,
|
||||
"against": self.employee,
|
||||
"cost_center": self.cost_center,
|
||||
"against_voucher_type": self.doctype,
|
||||
"against_voucher": self.name
|
||||
})
|
||||
)
|
||||
|
||||
def validate_account_details(self):
|
||||
if not self.cost_center:
|
||||
frappe.throw(_("Cost center is required to book an expense claim"))
|
||||
@ -190,9 +206,20 @@ class ExpenseClaim(AccountsController):
|
||||
if self.approval_status == 'Rejected':
|
||||
d.sanctioned_amount = 0.0
|
||||
|
||||
self.total_claimed_amount += flt(d.claim_amount)
|
||||
self.total_claimed_amount += flt(d.amount)
|
||||
self.total_sanctioned_amount += flt(d.sanctioned_amount)
|
||||
|
||||
def calculate_taxes(self):
|
||||
self.total_taxes_and_charges = 0
|
||||
for tax in self.taxes:
|
||||
if tax.rate:
|
||||
tax.tax_amount = flt(self.total_sanctioned_amount) * flt(tax.rate/100)
|
||||
|
||||
tax.total = flt(tax.tax_amount) + flt(self.total_sanctioned_amount)
|
||||
self.total_taxes_and_charges += flt(tax.tax_amount)
|
||||
|
||||
self.grand_total = flt(self.total_sanctioned_amount) + flt(self.total_taxes_and_charges) - flt(self.total_advance_amount)
|
||||
|
||||
def update_task(self):
|
||||
task = frappe.get_doc("Task", self.task)
|
||||
task.update_total_expense_claim()
|
||||
@ -224,7 +251,7 @@ class ExpenseClaim(AccountsController):
|
||||
|
||||
def validate_sanctioned_amount(self):
|
||||
for d in self.get('expenses'):
|
||||
if flt(d.sanctioned_amount) > flt(d.claim_amount):
|
||||
if flt(d.sanctioned_amount) > flt(d.amount):
|
||||
frappe.throw(_("Sanctioned Amount cannot be greater than Claim Amount in Row {0}.").format(d.idx))
|
||||
|
||||
def set_expense_account(self, validate=False):
|
||||
|
@ -17,7 +17,7 @@ QUnit.test("Test: Expense Claim [HR]", function (assert) {
|
||||
d.expense_date = '2017-08-01',
|
||||
d.expense_type = 'Test Expense Type 1',
|
||||
d.description = 'This is just to test Expense Claim',
|
||||
d.claim_amount = 2000,
|
||||
d.amount = 2000,
|
||||
d.sanctioned_amount=2000,
|
||||
refresh_field('expenses');
|
||||
},
|
||||
|
@ -6,6 +6,7 @@ import frappe
|
||||
import unittest
|
||||
from frappe.utils import random_string, nowdate
|
||||
from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry
|
||||
from erpnext.accounts.doctype.account.test_account import create_account
|
||||
|
||||
test_records = frappe.get_test_records('Expense Claim')
|
||||
test_dependencies = ['Employee']
|
||||
@ -26,7 +27,7 @@ class TestExpenseClaim(unittest.TestCase):
|
||||
task_name = frappe.db.get_value("Task", {"project": "_Test Project 1"})
|
||||
payable_account = get_payable_account("Wind Power LLC")
|
||||
|
||||
make_expense_claim(payable_account, 300, 200, "Wind Power LLC","Travel Expenses - WP", "_Test Project 1", task_name)
|
||||
make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP", "_Test Project 1", task_name)
|
||||
|
||||
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
|
||||
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
|
||||
@ -62,7 +63,8 @@ class TestExpenseClaim(unittest.TestCase):
|
||||
|
||||
def test_expense_claim_gl_entry(self):
|
||||
payable_account = get_payable_account("Wind Power LLC")
|
||||
expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP")
|
||||
taxes = generate_taxes()
|
||||
expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP", do_not_submit=True, taxes=taxes)
|
||||
expense_claim.submit()
|
||||
|
||||
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||
@ -72,7 +74,8 @@ class TestExpenseClaim(unittest.TestCase):
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
expected_values = dict((d[0], d) for d in [
|
||||
[payable_account, 0.0, 200.0],
|
||||
['CGST - WP',10.0, 0.0],
|
||||
[payable_account, 0.0, 210.0],
|
||||
["Travel Expenses - WP", 200.0, 0.0]
|
||||
])
|
||||
|
||||
@ -89,7 +92,7 @@ class TestExpenseClaim(unittest.TestCase):
|
||||
"payable_account": payable_account,
|
||||
"approval_status": "Rejected",
|
||||
"expenses":
|
||||
[{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "claim_amount": 300, "sanctioned_amount": 200 }]
|
||||
[{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "amount": 300, "sanctioned_amount": 200 }]
|
||||
})
|
||||
expense_claim.submit()
|
||||
|
||||
@ -100,22 +103,44 @@ class TestExpenseClaim(unittest.TestCase):
|
||||
self.assertEquals(len(gl_entry), 0)
|
||||
|
||||
def get_payable_account(company):
|
||||
return frappe.get_cached_value('Company', company, 'default_payable_account')
|
||||
return frappe.get_cached_value('Company', company, 'default_payable_account')
|
||||
|
||||
def make_expense_claim(payable_account,claim_amount, sanctioned_amount, company, account, project=None, task_name=None):
|
||||
expense_claim = frappe.get_doc({
|
||||
def generate_taxes():
|
||||
parent_account = frappe.db.get_value('Account',
|
||||
{'company': "Wind Power LLC", 'is_group':1, 'account_type': 'Tax'},
|
||||
'name')
|
||||
account = create_account(company="Wind Power LLC", account_name="CGST", account_type="Tax", parent_account=parent_account)
|
||||
return {'taxes':[{
|
||||
"account_head": account,
|
||||
"rate": 0,
|
||||
"description": "CGST",
|
||||
"tax_amount": 10,
|
||||
"total": 210
|
||||
}]}
|
||||
|
||||
def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None):
|
||||
expense_claim = {
|
||||
"doctype": "Expense Claim",
|
||||
"employee": "_T-Employee-00001",
|
||||
"payable_account": payable_account,
|
||||
"approval_status": "Approved",
|
||||
"company": company,
|
||||
"expenses":
|
||||
[{ "expense_type": "Travel", "default_account": account, "claim_amount": claim_amount, "sanctioned_amount": sanctioned_amount }]
|
||||
})
|
||||
[{"expense_type": "Travel",
|
||||
"default_account": account,
|
||||
"amount": amount,
|
||||
"sanctioned_amount": sanctioned_amount}]}
|
||||
if taxes:
|
||||
expense_claim.update(taxes)
|
||||
|
||||
expense_claim = frappe.get_doc(expense_claim)
|
||||
|
||||
if project:
|
||||
expense_claim.project = project
|
||||
if task_name:
|
||||
expense_claim.task = task_name
|
||||
|
||||
if do_not_submit:
|
||||
return expense_claim
|
||||
expense_claim.submit()
|
||||
return expense_claim
|
||||
|
@ -253,7 +253,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "claim_amount",
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@ -262,7 +262,7 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Claim Amount",
|
||||
"label": "Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "claim_amount",
|
||||
@ -360,7 +360,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-02-24 08:41:36.122565",
|
||||
"modified": "2019-06-10 08:41:36.122565",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Expense Claim Detail",
|
||||
|
@ -0,0 +1,105 @@
|
||||
{
|
||||
"autoname": "hash",
|
||||
"creation": "2019-06-03 11:42:33.123976",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"account_head",
|
||||
"cost_center",
|
||||
"col_break1",
|
||||
"rate",
|
||||
"description",
|
||||
"section_break_6",
|
||||
"tax_amount",
|
||||
"column_break_8",
|
||||
"total"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "col_break1",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "account_head",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Account Head",
|
||||
"oldfieldname": "account_head",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Account",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": ":Company",
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
"label": "Cost Center",
|
||||
"oldfieldname": "cost_center",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Cost Center"
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Description",
|
||||
"oldfieldname": "description",
|
||||
"oldfieldtype": "Small Text",
|
||||
"print_width": "300px",
|
||||
"reqd": 1,
|
||||
"width": "300px"
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fetch_from": "account_head.tax_rate",
|
||||
"fetch_if_empty": 1,
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Rate",
|
||||
"oldfieldname": "rate",
|
||||
"oldfieldtype": "Currency"
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "tax_amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Amount",
|
||||
"oldfieldname": "tax_amount",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "currency"
|
||||
},
|
||||
{
|
||||
"columns": 2,
|
||||
"fieldname": "total",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Total",
|
||||
"oldfieldname": "total",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_8",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"modified": "2019-06-20 12:01:33.919555",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Expense Taxes and Charges",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 1
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class ExpenseTaxesandCharges(Document):
|
||||
pass
|
@ -18,11 +18,11 @@ class VehicleLog(Document):
|
||||
if (service_detail.service_item or service_detail.type or service_detail.frequency or service_detail.expense_amount):
|
||||
if not (service_detail.service_item and service_detail.type and service_detail.frequency and service_detail.expense_amount):
|
||||
frappe.throw(_("Service Item,Type,frequency and expense amount are required"))
|
||||
|
||||
|
||||
def on_submit(self):
|
||||
frappe.db.sql("update `tabVehicle` set last_odometer=%s where license_plate=%s",
|
||||
(self.odometer, self.license_plate))
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_make_model(license_plate):
|
||||
vehicle=frappe.get_doc("Vehicle",license_plate)
|
||||
@ -41,7 +41,7 @@ def make_expense_claim(docname):
|
||||
for serdetail in vehicle_log.service_detail:
|
||||
total_exp_amt = total_exp_amt + serdetail.expense_amount
|
||||
return total_exp_amt
|
||||
|
||||
|
||||
vehicle_log = frappe.get_doc("Vehicle Log", docname)
|
||||
exp_claim = frappe.new_doc("Expense Claim")
|
||||
exp_claim.employee=vehicle_log.employee
|
||||
@ -52,6 +52,6 @@ def make_expense_claim(docname):
|
||||
exp_claim.append("expenses",{
|
||||
"expense_date":vehicle_log.date,
|
||||
"description":_("Vehicle Expenses"),
|
||||
"claim_amount":total_claim_amt
|
||||
"amount":total_claim_amt
|
||||
})
|
||||
return exp_claim.as_dict()
|
||||
|
@ -152,7 +152,7 @@ class TestProductionPlan(unittest.TestCase):
|
||||
make_bom(item = item, raw_materials = raw_materials)
|
||||
production_plan = create_production_plan(item_code = 'Production Item CUST')
|
||||
production_plan.make_material_request()
|
||||
material_request = frappe.get_value('Material Request Item', {'production_plan': production_plan.name}, 'parent')
|
||||
material_request = frappe.db.get_value('Material Request Item', {'production_plan': production_plan.name, 'item_code': 'CUST-0987'}, 'parent')
|
||||
mr = frappe.get_doc('Material Request', material_request)
|
||||
self.assertTrue(mr.material_request_type, 'Customer Provided')
|
||||
self.assertTrue(mr.customer, '_Test Customer')
|
||||
|
@ -602,6 +602,6 @@ erpnext.patches.v11_1.set_salary_details_submittable
|
||||
erpnext.patches.v11_1.rename_depends_on_lwp
|
||||
execute:frappe.delete_doc("Report", "Inactive Items")
|
||||
erpnext.patches.v11_1.delete_scheduling_tool
|
||||
erpnext.patches.v12_0.make_custom_fields_for_bank_remittance
|
||||
erpnext.patches.v12_0.make_custom_fields_for_bank_remittance #14-06-2019
|
||||
execute:frappe.delete_doc_if_exists("Page", "support-analytics")
|
||||
erpnext.patches.v12_0.make_item_manufacturer
|
@ -103,8 +103,8 @@ class TestTimesheet(unittest.TestCase):
|
||||
{
|
||||
"billable": 1,
|
||||
"activity_type": "_Test Activity Type",
|
||||
"from_type": now_datetime(),
|
||||
"hours": 3,
|
||||
"from_time": now_datetime(),
|
||||
"to_time": now_datetime() + datetime.timedelta(hours=3),
|
||||
"company": "_Test Company"
|
||||
}
|
||||
)
|
||||
@ -113,8 +113,8 @@ class TestTimesheet(unittest.TestCase):
|
||||
{
|
||||
"billable": 1,
|
||||
"activity_type": "_Test Activity Type",
|
||||
"from_type": now_datetime(),
|
||||
"hours": 3,
|
||||
"from_time": now_datetime(),
|
||||
"to_time": now_datetime() + datetime.timedelta(hours=3),
|
||||
"company": "_Test Company"
|
||||
}
|
||||
)
|
||||
|
@ -11,9 +11,6 @@ $(document).bind('toolbar_setup', function() {
|
||||
href="https://discuss.erpnext.com">Feedback</a></p>'
|
||||
|
||||
|
||||
$('.navbar-home').html('<img class="erpnext-icon" src="'+
|
||||
frappe.urllib.get_base_url()+'/assets/erpnext/images/erp-icon.svg" />');
|
||||
|
||||
$('[data-link="docs"]').attr("href", "https://erpnext.com/docs")
|
||||
$('[data-link="issues"]').attr("href", "https://github.com/frappe/erpnext/issues")
|
||||
|
||||
|
@ -144,7 +144,9 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
||||
item.discount_amount = flt(item_rate) * flt(item.discount_percentage) / 100;
|
||||
}
|
||||
|
||||
item.rate = flt((item.price_list_rate) - (item.discount_amount), precision('rate', item));
|
||||
if (item.discount_amount) {
|
||||
item.rate = flt((item.price_list_rate) - (item.discount_amount), precision('rate', item));
|
||||
}
|
||||
|
||||
this.calculate_taxes_and_totals();
|
||||
},
|
||||
|
@ -118,34 +118,13 @@ function get_filters(){
|
||||
"options": erpnext.get_presentation_currency_list()
|
||||
},
|
||||
{
|
||||
"fieldname":"cost_center",
|
||||
"fieldname": "cost_center",
|
||||
"label": __("Cost Center"),
|
||||
"fieldtype": "MultiSelect",
|
||||
get_data: function() {
|
||||
var cost_centers = frappe.query_report.get_filter_value("cost_center") || "";
|
||||
|
||||
const values = cost_centers.split(/\s*,\s*/).filter(d => d);
|
||||
const txt = cost_centers.match(/[^,\s*]*$/)[0] || '';
|
||||
let data = [];
|
||||
|
||||
frappe.call({
|
||||
type: "GET",
|
||||
method:'frappe.desk.search.search_link',
|
||||
async: false,
|
||||
no_spinner: true,
|
||||
args: {
|
||||
doctype: "Cost Center",
|
||||
txt: txt,
|
||||
filters: {
|
||||
"company": frappe.query_report.get_filter_value("company"),
|
||||
"name": ["not in", values]
|
||||
}
|
||||
},
|
||||
callback: function(r) {
|
||||
data = r.results;
|
||||
}
|
||||
"fieldtype": "MultiSelectList",
|
||||
get_data: function(txt) {
|
||||
return frappe.db.get_link_options('Cost Center', txt, {
|
||||
company: frappe.query_report.get_filter_value("company")
|
||||
});
|
||||
return data;
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -65,6 +65,9 @@ $.extend(erpnext, {
|
||||
},
|
||||
|
||||
get_dimension_filters: async function() {
|
||||
if (!frappe.model.can_read('Accounting Dimension')) {
|
||||
return [];
|
||||
}
|
||||
let dimensions = await frappe.db.get_list('Accounting Dimension', {
|
||||
fields: ['label', 'fieldname', 'document_type'],
|
||||
filters: {
|
||||
|
@ -14,8 +14,12 @@ erpnext.doctypes_with_dimensions.forEach((doctype) => {
|
||||
onload: function(frm) {
|
||||
dimension_filters.then((dimensions) => {
|
||||
dimensions.forEach((dimension) => {
|
||||
frm.set_query(dimension['fieldname'],{
|
||||
"is_group": 0
|
||||
frappe.model.with_doctype(dimension['document_type'], () => {
|
||||
if (frappe.meta.has_field(dimension['document_type'], 'is_group')) {
|
||||
frm.set_query(dimension['fieldname'], {
|
||||
"is_group": 0
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2,16 +2,13 @@
|
||||
// MIT License. See license.txt
|
||||
|
||||
frappe.ui.form.on('Website Theme', {
|
||||
apply_custom_theme(frm) {
|
||||
let custom_theme = frm.doc.custom_theme;
|
||||
custom_theme = custom_theme.split('\n');
|
||||
if (
|
||||
frm.doc.apply_custom_theme
|
||||
&& custom_theme.length === 2
|
||||
&& custom_theme[1].includes('frappe/public/scss/website')
|
||||
validate(frm) {
|
||||
let theme_scss = frm.doc.theme_scss;
|
||||
if (theme_scss.includes('frappe/public/scss/website')
|
||||
&& !theme_scss.includes('erpnext/public/scss/website')
|
||||
) {
|
||||
frm.set_value('custom_theme',
|
||||
`$primary: #7575ff;\n@import "frappe/public/scss/website";\n@import "erpnext/public/scss/website";`);
|
||||
frm.set_value('theme_scss',
|
||||
`${frm.doc.theme_scss}\n@import "erpnext/public/scss/website";`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -275,7 +275,7 @@ def make_custom_fields(update=True):
|
||||
],
|
||||
'Company': [
|
||||
dict(fieldname='hra_section', label='HRA Settings',
|
||||
fieldtype='Section Break', insert_after='asset_received_but_not_billed'),
|
||||
fieldtype='Section Break', insert_after='asset_received_but_not_billed', collapsible=1),
|
||||
dict(fieldname='basic_component', label='Basic Component',
|
||||
fieldtype='Link', options='Salary Component', insert_after='hra_section'),
|
||||
dict(fieldname='hra_component', label='HRA Component',
|
||||
|
@ -60,11 +60,8 @@ class Gstr1Report(object):
|
||||
else:
|
||||
for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
|
||||
invoice_details = self.invoices.get(inv)
|
||||
for key, items in items_based_on_rate.items():
|
||||
rate = key[0]
|
||||
account = key[1]
|
||||
|
||||
row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, account, items)
|
||||
for rate, items in items_based_on_rate.items():
|
||||
row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items)
|
||||
|
||||
if self.filters.get("type_of_business") == "CDNR":
|
||||
row.append("Y" if invoice_details.posting_date <= date(2017, 7, 1) else "N")
|
||||
@ -103,7 +100,7 @@ class Gstr1Report(object):
|
||||
for key, value in iteritems(b2cs_output):
|
||||
self.data.append(value)
|
||||
|
||||
def get_row_data_for_invoice(self, invoice, invoice_details, tax_rate, account, items):
|
||||
def get_row_data_for_invoice(self, invoice, invoice_details, tax_rate, items):
|
||||
row = []
|
||||
for fieldname in self.invoice_fields:
|
||||
if self.filters.get("type_of_business") == "CDNR" and fieldname == "invoice_value":
|
||||
@ -120,10 +117,8 @@ class Gstr1Report(object):
|
||||
taxable_value = 0
|
||||
for item_code, net_amount in self.invoice_items.get(invoice).items():
|
||||
if item_code in items:
|
||||
if self.item_tax_rate.get(invoice) and self.item_tax_rate.get(invoice, {}).get(item_code):
|
||||
item_tax_rate = self.item_tax_rate.get(invoice, {}).get(item_code)
|
||||
if account in item_tax_rate and tax_rate == item_tax_rate.get(account):
|
||||
taxable_value += abs(net_amount)
|
||||
if self.item_tax_rate.get(invoice) and tax_rate in self.item_tax_rate.get(invoice, {}).get(item_code):
|
||||
taxable_value += abs(net_amount)
|
||||
elif not self.item_tax_rate.get(invoice):
|
||||
taxable_value += abs(net_amount)
|
||||
|
||||
@ -214,8 +209,9 @@ class Gstr1Report(object):
|
||||
if d.item_tax_rate:
|
||||
item_tax_rate = json.loads(d.item_tax_rate)
|
||||
|
||||
if item_tax_rate:
|
||||
self.item_tax_rate.setdefault(d.parent, {}).setdefault(d.item_code, item_tax_rate)
|
||||
for account, rate in item_tax_rate.items():
|
||||
tax_rate_dict = self.item_tax_rate.setdefault(d.parent, {}).setdefault(d.item_code, [])
|
||||
tax_rate_dict.append(rate)
|
||||
|
||||
def get_items_based_on_tax_rate(self):
|
||||
self.tax_details = frappe.db.sql("""
|
||||
@ -255,7 +251,7 @@ class Gstr1Report(object):
|
||||
tax_rate *= 2
|
||||
|
||||
rate_based_dict = self.items_based_on_tax_rate\
|
||||
.setdefault(parent, {}).setdefault((tax_rate, account), [])
|
||||
.setdefault(parent, {}).setdefault(tax_rate, [])
|
||||
if item_code not in rate_based_dict:
|
||||
rate_based_dict.append(item_code)
|
||||
except ValueError:
|
||||
|
@ -43,11 +43,8 @@ class Gstr2Report(Gstr1Report):
|
||||
self.get_igst_invoices()
|
||||
for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
|
||||
invoice_details = self.invoices.get(inv)
|
||||
for key, items in items_based_on_rate.items():
|
||||
rate = key[0]
|
||||
account = key[1]
|
||||
|
||||
row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, account, items)
|
||||
for rate, items in items_based_on_rate.items():
|
||||
row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items)
|
||||
tax_amount = taxable_value * rate / 100
|
||||
if inv in self.igst_invoices:
|
||||
row += [tax_amount, 0, 0]
|
||||
|
@ -29,7 +29,20 @@ frappe.query_reports["HSN-wise-summary of outward supplies"] = {
|
||||
"placeholder":"Company GSTIN",
|
||||
"options": [""],
|
||||
"width": "80"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"from_date",
|
||||
"label": __("From Date"),
|
||||
"fieldtype": "Date",
|
||||
"width": "80"
|
||||
},
|
||||
{
|
||||
"fieldname":"to_date",
|
||||
"label": __("To Date"),
|
||||
"fieldtype": "Date",
|
||||
"width": "80"
|
||||
},
|
||||
|
||||
],
|
||||
onload: (report) => {
|
||||
fetch_gstins(report);
|
||||
|
@ -88,7 +88,9 @@ def get_conditions(filters):
|
||||
|
||||
for opts in (("company", " and company=%(company)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]):
|
||||
conditions += opts[1]
|
||||
|
||||
|
@ -581,8 +581,8 @@ def make_delivery_note(source_name, target_doc=None):
|
||||
|
||||
if item:
|
||||
target.cost_center = frappe.db.get_value("Project", source_parent.project, "cost_center") \
|
||||
or item.get("selling_cost_center") \
|
||||
or item_group.get("selling_cost_center")
|
||||
or item.get("buying_cost_center") \
|
||||
or item_group.get("buying_cost_center")
|
||||
|
||||
target_doc = get_mapped_doc("Sales Order", source_name, {
|
||||
"Sales Order": {
|
||||
|
@ -779,6 +779,17 @@ class POSCart {
|
||||
|
||||
const customer = this.frm.doc.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() {
|
||||
@ -1551,6 +1562,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() {
|
||||
for (const btn in this.add_class) {
|
||||
const class_name = this.add_class[btn];
|
||||
|
@ -170,9 +170,10 @@
|
||||
"label": "Company Description"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "sales_settings",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Sales"
|
||||
"label": "Sales Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "sales_monthly_history",
|
||||
@ -530,6 +531,7 @@
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "fixed_asset_depreciation_settings",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Fixed Asset Depreciation Settings"
|
||||
@ -602,6 +604,7 @@
|
||||
"options": "Role"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"description": "For reference only.",
|
||||
"fieldname": "company_info",
|
||||
"fieldtype": "Section Break",
|
||||
@ -708,7 +711,7 @@
|
||||
"icon": "fa fa-building",
|
||||
"idx": 1,
|
||||
"image_field": "company_logo",
|
||||
"modified": "2019-06-13 18:03:14.764423",
|
||||
"modified": "2019-06-14 14:36:11.363309",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Company",
|
||||
|
@ -20,8 +20,7 @@ class DeliveryTrip(Document):
|
||||
# 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.uom_conversion_factor = frappe.db.get_value("UOM Conversion Factor",
|
||||
{"from_uom": "Meter", "to_uom": self.default_distance_uom},
|
||||
"value")
|
||||
{"from_uom": "Meter", "to_uom": self.default_distance_uom}, "value")
|
||||
|
||||
def validate(self):
|
||||
self.validate_stop_addresses()
|
||||
@ -139,7 +138,7 @@ class DeliveryTrip(Document):
|
||||
# Include last leg in the final distance calculation
|
||||
self.uom = self.default_distance_uom
|
||||
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
|
||||
else:
|
||||
idx += len(route) - 1
|
||||
@ -358,8 +357,12 @@ def notify_customers(delivery_trip):
|
||||
email_recipients = []
|
||||
|
||||
for stop in delivery_trip.delivery_stops:
|
||||
contact_info = frappe.db.get_value("Contact", stop.contact,
|
||||
["first_name", "last_name", "email_id", "gender"], as_dict=1)
|
||||
contact_info = frappe.db.get_value("Contact", stop.contact, ["first_name", "last_name", "email_id"], 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:
|
||||
context.update(stop.as_dict())
|
||||
@ -369,9 +372,9 @@ def notify_customers(delivery_trip):
|
||||
dispatch_template = frappe.get_doc("Email Template", dispatch_template_name)
|
||||
|
||||
frappe.sendmail(recipients=contact_info.email_id,
|
||||
subject=dispatch_template.subject,
|
||||
message=frappe.render_template(dispatch_template.response, context),
|
||||
attachments=get_attachments(stop))
|
||||
subject=dispatch_template.subject,
|
||||
message=frappe.render_template(dispatch_template.response, context),
|
||||
attachments=get_attachments(stop))
|
||||
|
||||
stop.db_set("email_sent_to", contact_info.email_id)
|
||||
email_recipients.append(contact_info.email_id)
|
||||
@ -388,9 +391,7 @@ def get_attachments(delivery_stop):
|
||||
return []
|
||||
|
||||
dispatch_attachment = frappe.db.get_single_value("Delivery Settings", "dispatch_attachment")
|
||||
attachments = frappe.attach_print("Delivery Note",
|
||||
delivery_stop.delivery_note,
|
||||
file_name="Delivery Note",
|
||||
print_format=dispatch_attachment)
|
||||
attachments = frappe.attach_print("Delivery Note", delivery_stop.delivery_note,
|
||||
file_name="Delivery Note", print_format=dispatch_attachment)
|
||||
|
||||
return [attachments]
|
||||
|
@ -67,8 +67,6 @@ class Item(WebsiteGenerator):
|
||||
from frappe.model.naming import set_name_by_naming_series
|
||||
set_name_by_naming_series(self)
|
||||
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.name = self.item_code
|
||||
|
@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Delayed Item Report"] = {
|
||||
"filters": [
|
||||
{
|
||||
fieldname: "company",
|
||||
label: __("Company"),
|
||||
fieldtype: "Link",
|
||||
options: "Company",
|
||||
default: frappe.defaults.get_default("company"),
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
fieldname:"from_date",
|
||||
label: __("From Date"),
|
||||
fieldtype: "Date",
|
||||
default: frappe.datetime.month_start(),
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
fieldname:"to_date",
|
||||
label: __("To Date"),
|
||||
fieldtype: "Date",
|
||||
default: frappe.datetime.now_date(),
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
fieldname:"sales_order",
|
||||
label: __("Sales Order"),
|
||||
fieldtype: "Link",
|
||||
options: "Sales Order",
|
||||
},
|
||||
{
|
||||
fieldname:"customer",
|
||||
label: __("Customer"),
|
||||
fieldtype: "Link",
|
||||
options: "Customer",
|
||||
},
|
||||
{
|
||||
fieldname:"customer_group",
|
||||
label: __("Customer Group"),
|
||||
fieldtype: "Link",
|
||||
options: "Customer Group",
|
||||
},
|
||||
{
|
||||
fieldname:"item_group",
|
||||
label: __("Item Group"),
|
||||
fieldtype: "Link",
|
||||
options: "Item Group",
|
||||
},
|
||||
{
|
||||
fieldname:"based_on",
|
||||
label: __("Based On"),
|
||||
fieldtype: "Select",
|
||||
options: ["Delivery Note", "Sales Invoice"],
|
||||
default: "Delivery Note",
|
||||
reqd: 1
|
||||
},
|
||||
]
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"creation": "2019-06-17 12:45:07.324014",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"letter_head": "Gadgets International",
|
||||
"modified": "2019-06-17 12:45:07.324014",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delayed Item Report",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Delivery Note",
|
||||
"report_name": "Delayed Item Report",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Accounts User"
|
||||
},
|
||||
{
|
||||
"role": "Sales User"
|
||||
},
|
||||
{
|
||||
"role": "Stock Manager"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
},
|
||||
{
|
||||
"role": "Maintenance User"
|
||||
}
|
||||
]
|
||||
}
|
168
erpnext/stock/report/delayed_item_report/delayed_item_report.py
Normal file
168
erpnext/stock/report/delayed_item_report/delayed_item_report.py
Normal file
@ -0,0 +1,168 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import date_diff
|
||||
|
||||
def execute(filters=None, consolidated = False):
|
||||
data, columns = DelayedItemReport(filters).run()
|
||||
|
||||
return data, columns
|
||||
|
||||
class DelayedItemReport(object):
|
||||
def __init__(self, filters=None):
|
||||
self.filters = frappe._dict(filters or {})
|
||||
|
||||
def run(self):
|
||||
return self.get_columns(), self.get_data() or []
|
||||
|
||||
def get_data(self, consolidated=False):
|
||||
conditions = ""
|
||||
|
||||
doctype = self.filters.get("based_on")
|
||||
child_doc= "%s Item" % doctype
|
||||
|
||||
if doctype == "Sales Invoice":
|
||||
conditions = " and `tabSales Invoice`.update_stock = 1 and `tabSales Invoice`.is_pos = 0"
|
||||
|
||||
if self.filters.get("item_group"):
|
||||
conditions += " and `tab%s`.item_group = %s" % (child_doc,
|
||||
frappe.db.escape(self.filters.get("item_group")))
|
||||
|
||||
for field in ["customer", "customer_group", "company"]:
|
||||
if self.filters.get(field):
|
||||
conditions += " and `tab%s`.%s = %s" % (doctype,
|
||||
field, frappe.db.escape(self.filters.get(field)))
|
||||
|
||||
sales_order_field = "against_sales_order"
|
||||
if doctype == "Sales Invoice":
|
||||
sales_order_field = "sales_order"
|
||||
|
||||
if self.filters.get("sales_order"):
|
||||
conditions = " and `tab%s`.%s = '%s'" %(child_doc, sales_order_field, self.filters.get("sales_order"))
|
||||
|
||||
self.transactions = frappe.db.sql(""" SELECT `tab{child_doc}`.item_code, `tab{child_doc}`.item_name,
|
||||
`tab{child_doc}`.item_group, `tab{child_doc}`.qty, `tab{child_doc}`.rate, `tab{child_doc}`.amount,
|
||||
`tab{child_doc}`.so_detail, `tab{child_doc}`.{so_field} as sales_order,
|
||||
`tab{doctype}`.customer, `tab{doctype}`.posting_date, `tab{doctype}`.name, `tab{doctype}`.grand_total
|
||||
FROM `tab{child_doc}`, `tab{doctype}`
|
||||
WHERE
|
||||
`tab{child_doc}`.parent = `tab{doctype}`.name and `tab{doctype}`.docstatus = 1 and
|
||||
`tab{doctype}`.posting_date between %(from_date)s and %(to_date)s and
|
||||
`tab{child_doc}`.{so_field} is not null and `tab{child_doc}`.{so_field} != '' {cond}
|
||||
""".format(cond=conditions, doctype=doctype, child_doc=child_doc, so_field=sales_order_field), {
|
||||
'from_date': self.filters.get('from_date'),
|
||||
'to_date': self.filters.get('to_date')
|
||||
}, as_dict=1)
|
||||
|
||||
if self.transactions:
|
||||
self.filter_transactions_data(consolidated)
|
||||
|
||||
return self.transactions
|
||||
|
||||
def filter_transactions_data(self, consolidated=False):
|
||||
sales_orders = [d.sales_order for d in self.transactions]
|
||||
doctype = "Sales Order"
|
||||
filters = {'name': ('in', sales_orders)}
|
||||
|
||||
if not consolidated:
|
||||
sales_order_items = [d.so_detail for d in self.transactions]
|
||||
doctype = "Sales Order Item"
|
||||
filters = {'parent': ('in', sales_orders), 'name': ('in', sales_order_items)}
|
||||
|
||||
so_data = {}
|
||||
for d in frappe.get_all(doctype, filters = filters,
|
||||
fields = ["delivery_date", "parent", "name"]):
|
||||
key = d.name if consolidated else (d.parent, d.name)
|
||||
if key not in so_data:
|
||||
so_data.setdefault(key, d.delivery_date)
|
||||
|
||||
for row in self.transactions:
|
||||
key = row.sales_order if consolidated else (row.sales_order, row.so_detail)
|
||||
row.update({
|
||||
'delivery_date': so_data.get(key),
|
||||
'delayed_days': date_diff(row.posting_date, so_data.get(key))
|
||||
})
|
||||
|
||||
return self.transactions
|
||||
|
||||
def get_columns(self):
|
||||
based_on = self.filters.get("based_on")
|
||||
|
||||
return [{
|
||||
"label": _(based_on),
|
||||
"fieldname": "name",
|
||||
"fieldtype": "Link",
|
||||
"options": based_on,
|
||||
"width": 100
|
||||
},{
|
||||
"label": _("Customer"),
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"options": "Customer",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Expected Delivery Date"),
|
||||
"fieldname": "delivery_date",
|
||||
"fieldtype": "Date",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Actual Delivery Date"),
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Item Code"),
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"options": "Item",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Item Name"),
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Data",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Quantity"),
|
||||
"fieldname": "qty",
|
||||
"fieldtype": "Float",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Rate"),
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Currency",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Amount"),
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Delayed Days"),
|
||||
"fieldname": "delayed_days",
|
||||
"fieldtype": "Int",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Sales Order"),
|
||||
"fieldname": "sales_order",
|
||||
"fieldtype": "Link",
|
||||
"options": "Sales Order",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Customer PO"),
|
||||
"fieldname": "po_no",
|
||||
"fieldtype": "Data",
|
||||
"width": 100
|
||||
}]
|
@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Delayed Order Report"] = {
|
||||
"filters": [
|
||||
{
|
||||
fieldname: "company",
|
||||
label: __("Company"),
|
||||
fieldtype: "Link",
|
||||
options: "Company",
|
||||
default: frappe.defaults.get_default("company"),
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
fieldname:"from_date",
|
||||
label: __("From Date"),
|
||||
fieldtype: "Date",
|
||||
default: frappe.datetime.month_start(),
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
fieldname:"to_date",
|
||||
label: __("To Date"),
|
||||
fieldtype: "Date",
|
||||
default: frappe.datetime.now_date(),
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
fieldname:"sales_order",
|
||||
label: __("Sales Order"),
|
||||
fieldtype: "Link",
|
||||
options: "Sales Order",
|
||||
},
|
||||
{
|
||||
fieldname:"customer",
|
||||
label: __("Customer"),
|
||||
fieldtype: "Link",
|
||||
options: "Customer",
|
||||
},
|
||||
{
|
||||
fieldname:"customer_group",
|
||||
label: __("Customer Group"),
|
||||
fieldtype: "Link",
|
||||
options: "Customer Group",
|
||||
},
|
||||
{
|
||||
fieldname:"item_group",
|
||||
label: __("Item Group"),
|
||||
fieldtype: "Link",
|
||||
options: "Item Group",
|
||||
},
|
||||
{
|
||||
fieldname:"based_on",
|
||||
label: __("Based On"),
|
||||
fieldtype: "Select",
|
||||
options: ["Delivery Note", "Sales Invoice"],
|
||||
default: "Delivery Note",
|
||||
reqd: 1
|
||||
},
|
||||
]
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"creation": "2019-06-17 12:45:56.359322",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"letter_head": "Gadgets International",
|
||||
"modified": "2019-06-17 12:45:56.359322",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delayed Order Report",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Delivery Note",
|
||||
"report_name": "Delayed Order Report",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Accounts User"
|
||||
},
|
||||
{
|
||||
"role": "Sales User"
|
||||
},
|
||||
{
|
||||
"role": "Stock Manager"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
},
|
||||
{
|
||||
"role": "Maintenance User"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from frappe import _
|
||||
from erpnext.stock.report.delayed_item_report.delayed_item_report import DelayedItemReport
|
||||
|
||||
def execute(filters=None):
|
||||
columns, data = [], []
|
||||
|
||||
columns, data = DelayedOrderReport(filters).run()
|
||||
|
||||
return columns, data
|
||||
|
||||
class DelayedOrderReport(DelayedItemReport):
|
||||
def run(self):
|
||||
return self.get_columns(), self.get_data(consolidated=True) or []
|
||||
|
||||
def get_data(self, consolidated=False):
|
||||
data = super(DelayedOrderReport, self).get_data(consolidated) or []
|
||||
|
||||
so_list = []
|
||||
result = []
|
||||
for d in data:
|
||||
if d.sales_order not in so_list:
|
||||
so_list.append(d.sales_order)
|
||||
result.append(d)
|
||||
|
||||
return result
|
||||
|
||||
def get_columns(self):
|
||||
based_on = self.filters.get("based_on")
|
||||
|
||||
return [{
|
||||
"label": _(based_on),
|
||||
"fieldname": "name",
|
||||
"fieldtype": "Link",
|
||||
"options": based_on,
|
||||
"width": 100
|
||||
},{
|
||||
"label": _("Customer"),
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"options": "Customer",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Expected Delivery Date"),
|
||||
"fieldname": "delivery_date",
|
||||
"fieldtype": "Date",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Actual Delivery Date"),
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Amount"),
|
||||
"fieldname": "grand_total",
|
||||
"fieldtype": "Currency",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Delayed Days"),
|
||||
"fieldname": "delayed_days",
|
||||
"fieldtype": "Int",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Sales Order"),
|
||||
"fieldname": "sales_order",
|
||||
"fieldtype": "Link",
|
||||
"options": "Sales Order",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Customer PO"),
|
||||
"fieldname": "po_no",
|
||||
"fieldtype": "Data",
|
||||
"width": 100
|
||||
}]
|
@ -10,8 +10,23 @@ frappe.query_reports["Total Stock Summary"] = {
|
||||
"fieldtype": "Select",
|
||||
"width": "80",
|
||||
"reqd": 1,
|
||||
"options": ["","Warehouse", "Company"],
|
||||
"default": "Warehouse"
|
||||
"options": ["", "Warehouse", "Company"],
|
||||
"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",
|
||||
|
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
|
Loading…
x
Reference in New Issue
Block a user