Merge branch 'develop'

This commit is contained in:
mbauskar 2017-06-22 13:10:59 +05:30
commit 78aac9edb6
540 changed files with 35292 additions and 31643 deletions

136
.eslintrc Normal file
View File

@ -0,0 +1,136 @@
{
"env": {
"browser": true,
"node": true,
"es6": true
},
"extends": "eslint:recommended",
"rules": {
"indent": [
"error",
"tab",
{ "SwitchCase": 1 }
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"off"
],
"semi": [
"warn",
"always"
],
"camelcase": [
"off"
],
"no-unused-vars": [
"warn"
],
"no-redeclare": [
"warn"
],
"no-console": [
"warn"
],
"no-extra-boolean-cast": [
"off"
],
"no-control-regex": [
"off"
],
"spaced-comment": [
"warn"
],
"no-trailing-spaces": [
"warn"
]
},
"root": true,
"globals": {
"frappe": true,
"erpnext": true,
"schools": true,
"$": true,
"jQuery": true,
"moment": true,
"hljs": true,
"Awesomplete": true,
"CalHeatMap": true,
"Sortable": true,
"Showdown": true,
"Taggle": true,
"Gantt": true,
"Slick": true,
"PhotoSwipe": true,
"PhotoSwipeUI_Default": true,
"fluxify": true,
"io": true,
"c3": true,
"__": true,
"_p": true,
"_f": true,
"repl": true,
"Class": true,
"locals": true,
"cint": true,
"cstr": true,
"cur_frm": true,
"cur_dialog": true,
"cur_page": true,
"cur_list": true,
"cur_tree": true,
"msg_dialog": true,
"is_null": true,
"in_list": true,
"has_common": true,
"has_words": true,
"validate_email": true,
"get_number_format": true,
"format_number": true,
"format_currency": true,
"round_based_on_smallest_currency_fraction": true,
"roundNumber": true,
"comment_when": true,
"replace_newlines": true,
"open_url_post": true,
"toTitle": true,
"lstrip": true,
"strip": true,
"strip_html": true,
"replace_all": true,
"flt": true,
"precision": true,
"md5": true,
"CREATE": true,
"AMEND": true,
"CANCEL": true,
"copy_dict": true,
"get_number_format_info": true,
"print_table": true,
"Layout": true,
"web_form_settings": true,
"$c": true,
"$a": true,
"$i": true,
"$bg": true,
"$y": true,
"$c_obj": true,
"$c_obj_csv": true,
"refresh_many": true,
"refresh_field": true,
"toggle_field": true,
"get_field_obj": true,
"get_query_params": true,
"unhide_field": true,
"hide_field": true,
"set_field_options": true,
"getCookie": true,
"getCookies": true,
"get_url_arg": true,
"get_server_fields": true,
"set_multiple": true
}
}

View File

@ -2,7 +2,7 @@
from __future__ import unicode_literals
import frappe
__version__ = '8.0.51'
__version__ = '8.1.0'
def get_default_company(user=None):
@ -36,12 +36,13 @@ def get_company_currency(company):
frappe.flags.company_currency[company] = frappe.db.get_value('Company', company, 'default_currency')
return frappe.flags.company_currency[company]
def set_perpetual_inventory(enable=1, company=None):
if not company:
company = "_Test Company" if frappe.flags.in_test else get_default_company()
def set_perpetual_inventory(enable=1):
accounts_settings = frappe.get_doc("Accounts Settings")
accounts_settings.auto_accounting_for_stock = enable
accounts_settings.save()
company = frappe.get_doc("Company", company)
company.enable_perpetual_inventory = enable
company.save()
def encode_company_abbr(name, company):
'''Returns name encoded with company abbreviation'''
@ -53,4 +54,15 @@ def encode_company_abbr(name, company):
return " - ".join(parts)
def is_perpetual_inventory_enabled(company):
if not company:
company = "_Test Company" if frappe.flags.in_test else get_default_company()
if not hasattr(frappe.local, 'enable_perpetual_inventory'):
frappe.local.enable_perpetual_inventory = {}
if not company in frappe.local.enable_perpetual_inventory:
frappe.local.enable_perpetual_inventory[company] = frappe.db.get_value("Company",
company, "enable_perpetual_inventory") or 0
return frappe.local.enable_perpetual_inventory[company]

View File

@ -2,26 +2,26 @@
// License: GNU General Public License v3. See license.txt
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
if(doc.__islocal) {
msgprint(__("Please create new account from Chart of Accounts."));
cur_frm.cscript.refresh = function (doc, cdt, cdn) {
if (doc.__islocal) {
frappe.msgprint(__("Please create new account from Chart of Accounts."));
throw "cannot create";
}
cur_frm.toggle_display('account_name', doc.__islocal);
// hide fields if group
cur_frm.toggle_display(['account_type', 'tax_rate'], cint(doc.is_group)==0)
cur_frm.toggle_display(['account_type', 'tax_rate'], cint(doc.is_group) == 0)
// disable fields
cur_frm.toggle_enable(['account_name', 'is_group', 'company'], false);
if(cint(doc.is_group)==0) {
if (cint(doc.is_group) == 0) {
cur_frm.toggle_display('freeze_account', doc.__onload && doc.__onload.can_freeze_account);
}
// read-only for root accounts
if(!doc.parent_account) {
if (!doc.parent_account) {
cur_frm.set_read_only();
cur_frm.set_intro(__("This is a root account and cannot be edited."));
} else {
@ -38,53 +38,53 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) {
cur_frm.add_fetch('parent_account', 'report_type', 'report_type');
cur_frm.add_fetch('parent_account', 'root_type', 'root_type');
cur_frm.cscript.account_type = function(doc, cdt, cdn) {
if(doc.is_group==0) {
cur_frm.cscript.account_type = function (doc, cdt, cdn) {
if (doc.is_group == 0) {
cur_frm.toggle_display(['tax_rate'], doc.account_type == 'Tax');
cur_frm.toggle_display('warehouse', doc.account_type=='Stock');
cur_frm.toggle_display('warehouse', doc.account_type == 'Stock');
}
}
cur_frm.cscript.add_toolbar_buttons = function(doc) {
cur_frm.cscript.add_toolbar_buttons = function (doc) {
cur_frm.add_custom_button(__('Chart of Accounts'),
function() { frappe.set_route("Tree", "Account"); });
function () { frappe.set_route("Tree", "Account"); });
if (doc.is_group == 1) {
cur_frm.add_custom_button(__('Group to Non-Group'),
function() { cur_frm.cscript.convert_to_ledger(); }, 'fa fa-retweet', 'btn-default');
function () { cur_frm.cscript.convert_to_ledger(); }, 'fa fa-retweet', 'btn-default');
} else if (cint(doc.is_group) == 0) {
cur_frm.add_custom_button(__('Ledger'), function() {
cur_frm.add_custom_button(__('Ledger'), function () {
frappe.route_options = {
"account": doc.name,
"from_date": sys_defaults.year_start_date,
"to_date": sys_defaults.year_end_date,
"from_date": frappe.sys_defaults.year_start_date,
"to_date": frappe.sys_defaults.year_end_date,
"company": doc.company
};
frappe.set_route("query-report", "General Ledger");
});
cur_frm.add_custom_button(__('Non-Group to Group'),
function() { cur_frm.cscript.convert_to_group(); }, 'fa fa-retweet', 'btn-default')
function () { cur_frm.cscript.convert_to_group(); }, 'fa fa-retweet', 'btn-default')
}
}
cur_frm.cscript.convert_to_ledger = function(doc, cdt, cdn) {
return $c_obj(cur_frm.doc,'convert_group_to_ledger','',function(r,rt) {
if(r.message == 1) {
cur_frm.refresh();
}
});
cur_frm.cscript.convert_to_ledger = function (doc, cdt, cdn) {
return $c_obj(cur_frm.doc, 'convert_group_to_ledger', '', function (r, rt) {
if (r.message == 1) {
cur_frm.refresh();
}
});
}
cur_frm.cscript.convert_to_group = function(doc, cdt, cdn) {
return $c_obj(cur_frm.doc,'convert_ledger_to_group','',function(r,rt) {
if(r.message == 1) {
cur_frm.refresh();
}
});
cur_frm.cscript.convert_to_group = function (doc, cdt, cdn) {
return $c_obj(cur_frm.doc, 'convert_ledger_to_group', '', function (r, rt) {
if (r.message == 1) {
cur_frm.refresh();
}
});
}
cur_frm.fields_dict['parent_account'].get_query = function(doc) {
cur_frm.fields_dict['parent_account'].get_query = function (doc) {
return {
filters: {
"is_group": 1,

View File

@ -1,5 +1,6 @@
{
"allow_copy": 1,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"beta": 0,
@ -402,35 +403,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "warehouse",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Warehouse",
"length": 0,
"no_copy": 0,
"options": "Warehouse",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@ -545,18 +517,18 @@
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-money",
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-02-17 16:22:49.249075",
"modified": "2017-04-21 17:22:41.150984",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account",

View File

@ -35,7 +35,6 @@ class Account(Document):
self.validate_group_or_ledger()
self.set_root_and_report_type()
self.validate_mandatory()
self.validate_warehouse_account()
self.validate_frozen_accounts_modifier()
self.validate_balance_must_be_debit_or_credit()
self.validate_account_currency()
@ -162,46 +161,6 @@ class Account(Document):
if not self.report_type:
throw(_("Report Type is mandatory"))
def validate_warehouse_account(self):
'''If perpetual inventory is set, and warehouse is linked,
the account balance and stock balance as of now must always match.
'''
from erpnext.accounts.utils import get_balance_on
from erpnext.stock.utils import get_stock_value_on
if not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
return
if self.account_type == "Stock":
if self.is_group == 0 and not self.warehouse:
frappe.throw(_("Warehouse is mandatory for non group Accounts of type Stock"))
if self.warehouse:
# company must be same
if frappe.get_value('Warehouse', self.warehouse, 'company') != self.company:
frappe.throw(_("Warehouse company must be same as Account company"))
# balance must be same
stock_balance = get_stock_value_on(self.warehouse)
if self.is_new():
account_balance = 0.0
else:
account_balance = get_balance_on(self.name)
if account_balance != stock_balance:
frappe.throw(_('Account balance ({0}) for {1} and stock value ({2}) for warehouse {3} must be same')
.format(fmt_money(account_balance, currency=self.account_currency), self.name,
fmt_money(stock_balance, currency=self.account_currency), self.warehouse))
elif self.warehouse:
self.warehouse = None
def validate_warehouse(self, warehouse):
lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])
if lft and rgt:
if frappe.db.sql_list("""select sle.name from `tabStock Ledger Entry` sle where exists (select wh.name from
tabWarehouse wh where lft >= %s and rgt <= %s and sle.warehouse = wh.name)""", (lft, rgt)):
throw(_("Stock entries exist against Warehouse {0}, hence you cannot re-assign or modify it").format(warehouse))
def update_nsm_model(self):
"""update lft, rgt indices for nested set model"""

View File

@ -37,16 +37,6 @@ frappe.treeview_settings["Account"] = {
},
{fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate'),
depends_on: 'eval:doc.is_group==0&&doc.account_type=="Tax"'},
{fieldtype:'Link', fieldname:'warehouse', label:__('Warehouse'), options:"Warehouse",
depends_on: 'eval:(!doc.is_group&&doc.account_type=="Stock")',
get_query: function() {
return {
filters:{
"company": frappe.treeview_settings.filters["company"]
}
}
}
},
{fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency",
description: __("Optional. Sets company's default currency, if not specified.")}
],
@ -72,8 +62,8 @@ frappe.treeview_settings["Account"] = {
click: function(node, btn) {
frappe.route_options = {
"account": node.label,
"from_date": sys_defaults.year_start_date,
"to_date": sys_defaults.year_end_date,
"from_date": frappe.sys_defaults.year_start_date,
"to_date": frappe.sys_defaults.year_end_date,
"company": frappe.defaults.get_default('company') ? frappe.defaults.get_default('company'): ""
};
frappe.set_route("query-report", "General Ledger");

View File

@ -117,7 +117,7 @@ def get_charts_for_country(country):
def get_account_tree_from_existing_company(existing_company):
all_accounts = frappe.get_all('Account',
filters={'company': existing_company, "warehouse": ""},
filters={'company': existing_company},
fields = ["name", "account_name", "parent_account", "account_type",
"is_group", "root_type", "tax_rate"],
order_by="lft, rgt")

View File

@ -88,7 +88,6 @@
"Items Delivered to Customs on temprary Base": {}
},
"Stock in Hand": {
"is_group": 1,
"account_type": "Stock"
}
},

View File

@ -65,8 +65,9 @@
"account_type": "Fixed Asset"
},
"Stock": {
"account_type": "Stock",
"is_group": 1
"Stock in Hand": {
"account_type": "Stock"
}
},
"root_type": "Asset"
},

View File

@ -26,8 +26,9 @@
"Earnest Money": {}
},
"Stock Assets": {
"account_type": "Stock",
"is_group": 1
"Stock in Hand": {
"account_type": "Stock"
}
},
"Tax Assets": {
"is_group": 1

View File

@ -40,8 +40,9 @@
"Rental Deposits": {}
},
"Stock Assets": {
"is_group": 1,
"account_type": "Stock"
"Stock in Hand": {
"account_type": "Stock"
}
},
"Tax Assets": {
"GST-Input": {}

View File

@ -40,8 +40,9 @@
"Rental Deposits": {}
},
"Stock Assets": {
"account_type": "Stock",
"is_group": 1
"Stock in Hand": {
"account_type": "Stock"
}
},
"Tax Assets": {
"GST-Input": {}

View File

@ -30,8 +30,10 @@ def get():
_("Earnest Money"): {}
},
_("Stock Assets"): {
_("Stock In Hand"): {
"account_type": "Stock"
},
"account_type": "Stock",
"is_group": 1
},
_("Tax Assets"): {
"is_group": 1

View File

@ -3,6 +3,8 @@
from __future__ import unicode_literals
import frappe
from erpnext.stock import get_warehouse_account, get_company_default_inventory_account
def _make_test_records(verbose):
from frappe.test_runner import make_test_objects
@ -63,3 +65,24 @@ def _make_test_records(verbose):
} for account_name, parent_account, is_group, account_type, currency in accounts])
return test_objects
def get_inventory_account(company, warehouse=None):
account = None
if warehouse:
account = get_warehouse_account(warehouse, company)
else:
account = get_company_default_inventory_account(company)
return account
def create_account(**kwargs):
account = frappe.get_doc(dict(
doctype = "Account",
account_name = kwargs.get('account_name'),
account_type = kwargs.get('account_type'),
parent_account = kwargs.get('parent_account'),
company = kwargs.get('company')
))
account.save()
return account.name

View File

@ -14,6 +14,7 @@
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -22,7 +23,7 @@
"description": "If enabled, the system will post accounting entries for inventory automatically.",
"fieldname": "auto_accounting_for_stock",
"fieldtype": "Check",
"hidden": 0,
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
@ -44,6 +45,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -73,6 +75,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -103,6 +106,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -131,6 +135,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -161,6 +166,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -190,6 +196,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -219,6 +226,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -249,6 +257,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -290,7 +299,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2017-04-18 13:35:59.166250",
"modified": "2017-06-16 17:39:50.614522",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@ -11,24 +11,4 @@ from frappe.model.document import Document
class AccountsSettings(Document):
def on_update(self):
frappe.db.set_default("auto_accounting_for_stock", self.auto_accounting_for_stock)
if cint(self.auto_accounting_for_stock):
# set default perpetual account in company
for company in frappe.db.sql("select name from tabCompany"):
company = frappe.get_doc("Company", company[0])
company.flags.ignore_permissions = True
company.save()
# Create account head for warehouses
warehouse_list = frappe.db.sql("""select name, company from tabWarehouse
where disabled=0""", as_dict=1)
warehouse_with_no_company = [d.name for d in warehouse_list if not d.company]
if warehouse_with_no_company:
frappe.throw(_("Company is missing in warehouses {0}")
.format(comma_and(warehouse_with_no_company)))
for wh in warehouse_list:
wh_doc = frappe.get_doc("Warehouse", wh.name)
wh_doc.flags.ignore_permissions = True
wh_doc.save()
pass

View File

@ -14,7 +14,7 @@ frappe.ui.form.on('Asset', {
}
};
});
frm.set_query("warehouse", function() {
return {
"filters": {
@ -24,12 +24,12 @@ frappe.ui.form.on('Asset', {
};
});
},
refresh: function(frm) {
frappe.ui.form.trigger("Asset", "is_existing_asset");
frm.toggle_display("next_depreciation_date", frm.doc.docstatus < 1);
frm.events.make_schedules_editable(frm);
if (frm.doc.docstatus==1) {
if (frm.doc.status=='Submitted' && !frm.doc.is_existing_asset && !frm.doc.purchase_invoice) {
frm.add_custom_button("Make Purchase Invoice", function() {
@ -40,60 +40,60 @@ frappe.ui.form.on('Asset', {
frm.add_custom_button("Transfer Asset", function() {
erpnext.asset.transfer_asset(frm);
});
frm.add_custom_button("Scrap Asset", function() {
erpnext.asset.scrap_asset(frm);
});
frm.add_custom_button("Sell Asset", function() {
erpnext.asset.make_sales_invoice(frm);
});
} else if (frm.doc.status=='Scrapped') {
frm.add_custom_button("Restore Asset", function() {
erpnext.asset.restore_asset(frm);
});
}
frm.trigger("show_graph");
}
},
show_graph: function(frm) {
show_graph: function(frm) {
var x_intervals = ["x", frm.doc.purchase_date];
var asset_values = ["Asset Value", frm.doc.gross_purchase_amount];
var last_depreciation_date = frm.doc.purchase_date;
if(frm.doc.opening_accumulated_depreciation) {
last_depreciation_date = frappe.datetime.add_months(frm.doc.next_depreciation_date,
last_depreciation_date = frappe.datetime.add_months(frm.doc.next_depreciation_date,
-1*frm.doc.frequency_of_depreciation);
x_intervals.push(last_depreciation_date);
asset_values.push(flt(frm.doc.gross_purchase_amount) -
asset_values.push(flt(frm.doc.gross_purchase_amount) -
flt(frm.doc.opening_accumulated_depreciation));
}
$.each(frm.doc.schedules || [], function(i, v) {
x_intervals.push(v.schedule_date);
asset_value = flt(frm.doc.gross_purchase_amount) - flt(v.accumulated_depreciation_amount);
if(v.journal_entry) {
var asset_value = flt(frm.doc.gross_purchase_amount) - flt(v.accumulated_depreciation_amount);
if(v.journal_entry) {
last_depreciation_date = v.schedule_date;
asset_values.push(asset_value)
} else {
if (in_list(["Scrapped", "Sold"], frm.doc.status)) {
asset_values.push(null)
asset_values.push(null)
} else {
asset_values.push(asset_value)
}
}
})
if(in_list(["Scrapped", "Sold"], frm.doc.status)) {
x_intervals.push(frm.doc.disposal_date);
asset_values.push(0);
last_depreciation_date = frm.doc.disposal_date;
}
frm.dashboard.setup_chart({
data: {
x: 'x',
@ -117,9 +117,9 @@ frappe.ui.form.on('Asset', {
padding: {bottom: 10}
}
}
});
});
},
item_code: function(frm) {
if(frm.doc.item_code) {
frappe.call({
@ -137,27 +137,27 @@ frappe.ui.form.on('Asset', {
})
}
},
is_existing_asset: function(frm) {
frm.toggle_enable("supplier", frm.doc.is_existing_asset);
frm.toggle_reqd("next_depreciation_date", !frm.doc.is_existing_asset);
},
opening_accumulated_depreciation: function(frm) {
erpnext.asset.set_accululated_depreciation(frm);
},
depreciation_method: function(frm) {
frm.events.make_schedules_editable(frm);
},
make_schedules_editable: function(frm) {
var is_editable = frm.doc.depreciation_method==="Manual" ? true : false;
frm.toggle_enable("schedules", is_editable);
frm.fields_dict["schedules"].grid.toggle_enable("schedule_date", is_editable);
frm.fields_dict["schedules"].grid.toggle_enable("depreciation_amount", is_editable);
}
});
frappe.ui.form.on('Depreciation Schedule', {
@ -177,7 +177,7 @@ frappe.ui.form.on('Depreciation Schedule', {
})
}
},
depreciation_amount: function(frm, cdt, cdn) {
erpnext.asset.set_accululated_depreciation(frm);
}
@ -186,11 +186,11 @@ frappe.ui.form.on('Depreciation Schedule', {
erpnext.asset.set_accululated_depreciation = function(frm) {
if(frm.doc.depreciation_method != "Manual") return;
accumulated_depreciation = flt(frm.doc.opening_accumulated_depreciation);
var accumulated_depreciation = flt(frm.doc.opening_accumulated_depreciation);
$.each(frm.doc.schedules || [], function(i, row) {
accumulated_depreciation += flt(row.depreciation_amount);
frappe.model.set_value(row.doctype, row.name,
frappe.model.set_value(row.doctype, row.name,
"accumulated_depreciation_amount", accumulated_depreciation);
})
}
@ -260,9 +260,9 @@ erpnext.asset.transfer_asset = function(frm) {
title: __("Transfer Asset"),
fields: [
{
"label": __("Target Warehouse"),
"label": __("Target Warehouse"),
"fieldname": "target_warehouse",
"fieldtype": "Link",
"fieldtype": "Link",
"options": "Warehouse",
"get_query": function () {
return {
@ -271,13 +271,13 @@ erpnext.asset.transfer_asset = function(frm) {
["Warehouse", "is_group", "=", 0]
]
}
},
"reqd": 1
},
"reqd": 1
},
{
"label": __("Date"),
"label": __("Date"),
"fieldname": "transfer_date",
"fieldtype": "Datetime",
"fieldtype": "Datetime",
"reqd": 1,
"default": frappe.datetime.now_datetime()
}
@ -285,7 +285,7 @@ erpnext.asset.transfer_asset = function(frm) {
});
dialog.set_primary_action(__("Transfer"), function() {
args = dialog.get_values();
var args = dialog.get_values();
if(!args) return;
dialog.hide();
return frappe.call({

View File

@ -19,7 +19,8 @@ class Asset(Document):
self.validate_asset_values()
self.make_depreciation_schedule()
self.set_accumulated_depreciation()
self.validate_expected_value_after_useful_life()
if self.get("schedules"):
self.validate_expected_value_after_useful_life()
# Validate depreciation related accounts
get_depreciation_accounts(self)

View File

@ -1,3 +1,3 @@
frappe.listview_settings['Asset'] = {
add_fields: ['image']
add_fields: ['image']
}

View File

@ -4,7 +4,7 @@
frappe.ui.form.on('Asset Movement', {
onload: function(frm) {
frm.add_fetch("asset", "warehouse", "source_warehouse");
frm.set_query("target_warehouse", function() {
return {
filters: [

View File

@ -20,11 +20,11 @@ frappe.ui.form.on('Bank Guarantee', {
});
},
start_date: function(frm) {
end_date = frappe.datetime.add_days(cur_frm.doc.start_date, cur_frm.doc.validity - 1);
var end_date = frappe.datetime.add_days(cur_frm.doc.start_date, cur_frm.doc.validity - 1);
cur_frm.set_value("end_date", end_date);
},
validity: function(frm) {
end_date = frappe.datetime.add_days(cur_frm.doc.start_date, cur_frm.doc.validity - 1);
var end_date = frappe.datetime.add_days(cur_frm.doc.start_date, cur_frm.doc.validity - 1);
cur_frm.set_value("end_date", end_date);
}
});

View File

@ -43,7 +43,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) {
cur_frm.cscript.parent_cost_center = function(doc, cdt, cdn) {
if(!doc.company){
msgprint(__('Please enter company name first'));
frappe.msgprint(__('Please enter company name first'));
}
}

View File

@ -13,7 +13,7 @@ $.extend(cur_frm.cscript, {
this.frm.toggle_enable('year_start_date', doc.__islocal)
this.frm.toggle_enable('year_end_date', doc.__islocal)
if (!doc.__islocal && (doc.name != sys_defaults.fiscal_year)) {
if (!doc.__islocal && (doc.name != frappe.sys_defaults.fiscal_year)) {
this.frm.add_custom_button(__("Default"),
this.frm.cscript.set_as_default, "fa fa-star");
this.frm.set_intro(__("To set this Fiscal Year as Default, click on 'Set as Default'"));
@ -30,7 +30,7 @@ $.extend(cur_frm.cscript, {
year_start_date: function(doc, dt, dn) {
var me = this;
year_end_date =
var year_end_date =
frappe.datetime.add_days(frappe.datetime.add_months(this.frm.doc.year_start_date, 12), -1);
this.frm.set_value("year_end_date", year_end_date);
},

View File

@ -62,11 +62,10 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
if(this.frm.doc.__islocal && this.frm.doc.company) {
frappe.model.set_default_values(this.frm.doc);
$.each(this.frm.doc.accounts || [], function(i, jvd) {
frappe.model.set_default_values(jvd);
}
);
frappe.model.set_default_values(jvd);
});
if(!this.frm.doc.amended_from) this.frm.doc.posting_date = this.frm.posting_date || get_today();
if(!this.frm.doc.amended_from) this.frm.doc.posting_date = this.frm.posting_date || frappe.datetime.get_today();
}
},
@ -124,7 +123,7 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
// account filter
frappe.model.validate_missing(jvd, "account");
party_account_field = jvd.reference_type==="Sales Invoice" ? "debit_to": "credit_to";
var party_account_field = jvd.reference_type==="Sales Invoice" ? "debit_to": "credit_to";
out.filters.push([jvd.reference_type, party_account_field, "=", jvd.account]);
} else {
// party_type and party mandatory
@ -243,7 +242,7 @@ cur_frm.cscript.update_totals = function(doc) {
cur_frm.cscript.get_balance = function(doc,dt,dn) {
cur_frm.cscript.update_totals(doc);
return $c_obj(cur_frm.doc, 'get_balance', '', function(r, rt){
cur_frm.refresh();
cur_frm.refresh();
});
}

View File

@ -1311,7 +1311,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:inList([\"Credit Note\", \"Debit Note\"], doc.voucher_type)",
"depends_on": "eval:in_list([\"Credit Note\", \"Debit Note\"], doc.voucher_type)",
"fieldname": "stock_entry",
"fieldtype": "Link",
"hidden": 0,

View File

@ -871,14 +871,9 @@ def get_exchange_rate(posting_date, account=None, account_currency=None, company
if reference_type in ("Sales Invoice", "Purchase Invoice") and reference_name:
exchange_rate = frappe.db.get_value(reference_type, reference_name, "conversion_rate")
elif account_details and account_details.account_type == "Bank" and \
((account_details.root_type == "Asset" and flt(credit) > 0) or
(account_details.root_type == "Liability" and debit)):
exchange_rate = get_average_exchange_rate(account)
# The date used to retreive the exchange rate here is the date passed
# in as an argument to this function.
if not exchange_rate and account_currency and posting_date:
elif (not exchange_rate or exchange_rate==1) and account_currency and posting_date:
exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
else:
exchange_rate = 1

View File

@ -4,6 +4,7 @@
from __future__ import unicode_literals
import unittest, frappe
from frappe.utils import flt
from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.exceptions import InvalidAccountCurrency
@ -83,7 +84,8 @@ class TestJournalEntry(unittest.TestCase):
jv = frappe.copy_doc(test_records[0])
jv.get("accounts")[0].update({
"account": "_Test Warehouse - _TC",
"account": get_inventory_account('_Test Company'),
"company": "_Test Company",
"party_type": None,
"party": None
})

View File

@ -25,4 +25,5 @@ class ModeofPayment(Document):
for entry in self.accounts:
"""Error when Company of Ledger account doesn't match with Company Selected"""
if frappe.db.get_value("Account", entry.default_account, "company") != entry.company:
frappe.throw(_("Account does not match with Company"))
frappe.throw(_("Account {0} does not match with Company {1} in Mode of Account: {2}")
.format(entry.default_account, entry.company, self.name))

View File

@ -2,13 +2,13 @@
// License: GNU General Public License v3. See license.txt
cur_frm.cscript.onload = function(doc,cdt,cdn){
if(doc.__islocal){
var callback1 = function(r,rt){
refresh_field('percentages');
}
if(doc.__islocal){
var callback1 = function(r,rt){
refresh_field('percentages');
}
return $c('runserverobj',args={'method':'get_months', 'docs':doc}, callback1);
}
return $c('runserverobj', {'method':'get_months', 'docs':doc}, callback1);
}
}
cur_frm.cscript.refresh = function(doc,cdt,cdn){

View File

@ -36,7 +36,7 @@ frappe.ui.form.on('Payment Entry', {
frm.set_query("paid_to", function() {
var party_account_type = frm.doc.party_type=="Customer" ? "Receivable" : "Payable";
var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ?
["Bank", "Cash"] : party_account_type;
["Bank", "Cash"] : party_account_type;
return {
filters: {
@ -196,8 +196,8 @@ frappe.ui.form.on('Payment Entry', {
if(frm.doc.payment_type == "Internal Transfer") {
$.each(["party", "party_balance", "paid_from", "paid_to",
"references", "total_allocated_amount"], function(i, field) {
frm.set_value(field, null);
})
frm.set_value(field, null);
});
} else {
if(!frm.doc.party)
frm.set_value("party_type", frm.doc.payment_type=="Receive" ? "Customer" : "Supplier");
@ -214,9 +214,10 @@ frappe.ui.form.on('Payment Entry', {
$.each(["party", "party_balance", "paid_from", "paid_to",
"paid_from_account_currency", "paid_from_account_balance",
"paid_to_account_currency", "paid_to_account_balance",
"references", "total_allocated_amount"], function(i, field) {
"references", "total_allocated_amount"],
function(i, field) {
frm.set_value(field, null);
})
})
}
},
@ -331,10 +332,12 @@ frappe.ui.form.on('Payment Entry', {
frm.set_value("source_exchange_rate", 1);
} else if (frm.doc.paid_from){
if (in_list(["Internal Transfer", "Pay"], frm.doc.payment_type)) {
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_average_exchange_rate",
method: "erpnext.setup.utils.get_exchange_rate",
args: {
account: frm.doc.paid_from
from_currency: frm.doc.paid_from_account_currency,
to_currency: company_currency
},
callback: function(r, rt) {
frm.set_value("source_exchange_rate", r.message);
@ -501,13 +504,17 @@ frappe.ui.form.on('Payment Entry', {
}
});
if((frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier")) {
if(total_positive_outstanding > total_negative_outstanding)
frm.set_value("paid_amount",
total_positive_outstanding - total_negative_outstanding);
} else if (total_negative_outstanding &&
(total_positive_outstanding < total_negative_outstanding)) {
if(
(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier")
) {
if(total_positive_outstanding > total_negative_outstanding)
frm.set_value("paid_amount",
total_positive_outstanding - total_negative_outstanding);
} else if (
total_negative_outstanding &&
total_positive_outstanding < total_negative_outstanding
) {
frm.set_value("received_amount",
total_negative_outstanding - total_positive_outstanding);
}
@ -575,9 +582,11 @@ frappe.ui.form.on('Payment Entry', {
row.allocated_amount = 0 //If allocate payment amount checkbox is unchecked, set zero to allocate amount
if(frm.doc.allocate_payment_amount){
if(row.outstanding_amount > 0 && allocated_positive_outstanding > 0) {
if(row.outstanding_amount >= allocated_positive_outstanding)
row.allocated_amount = allocated_positive_outstanding;
else row.allocated_amount = row.outstanding_amount;
if(row.outstanding_amount >= allocated_positive_outstanding) {
row.allocated_amount = allocated_positive_outstanding;
} else {
row.allocated_amount = row.outstanding_amount;
}
allocated_positive_outstanding -= flt(row.allocated_amount);
} else if (row.outstanding_amount < 0 && allocated_negative_outstanding) {
@ -595,7 +604,8 @@ frappe.ui.form.on('Payment Entry', {
},
set_total_allocated_amount: function(frm) {
var total_allocated_amount = base_total_allocated_amount = 0.0;
var total_allocated_amount = 0.0;
var base_total_allocated_amount = 0.0;
$.each(frm.doc.references || [], function(i, row) {
if (row.allocated_amount) {
total_allocated_amount += flt(row.allocated_amount);
@ -667,19 +677,21 @@ frappe.ui.form.on('Payment Entry', {
return;
}
if(frm.doc.party_type=="Customer"
&& !in_list(["Sales Order", "Sales Invoice", "Journal Entry"], row.reference_doctype)) {
frappe.model.set_value(row.doctype, row.name, "reference_doctype", null);
frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice or Journal Entry", [row.idx]));
return false;
}
if(frm.doc.party_type=="Customer" &&
!in_list(["Sales Order", "Sales Invoice", "Journal Entry"], row.reference_doctype)
) {
frappe.model.set_value(row.doctype, row.name, "reference_doctype", null);
frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice or Journal Entry", [row.idx]));
return false;
}
if(frm.doc.party_type=="Supplier" && !in_list(["Purchase Order",
"Purchase Invoice", "Journal Entry"], row.reference_doctype)) {
frappe.model.set_value(row.doctype, row.name, "against_voucher_type", null);
frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry", [row.idx]));
return false;
}
if(frm.doc.party_type=="Supplier" &&
!in_list(["Purchase Order", "Purchase Invoice", "Journal Entry"], row.reference_doctype)
) {
frappe.model.set_value(row.doctype, row.name, "against_voucher_type", null);
frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry", [row.idx]));
return false;
}
}
if (row) {

View File

@ -48,6 +48,8 @@ class PaymentEntry(AccountsController):
self.validate_transaction_reference()
self.set_title()
self.set_remarks()
self.validate_duplicate_entry()
self.validate_allocated_amount()
def on_submit(self):
self.setup_party_account_field()
@ -61,7 +63,22 @@ class PaymentEntry(AccountsController):
self.make_gl_entries(cancel=1)
self.update_advance_paid()
self.delink_advance_entry_references()
def validate_duplicate_entry(self):
reference_names = []
for d in self.get("references"):
if (d.reference_doctype, d.reference_name) in reference_names:
frappe.throw(_("Row #{0}: Duplicate entry in References {1} {2}").format(d.idx, d.reference_doctype, d.reference_name))
reference_names.append((d.reference_doctype, d.reference_name))
def validate_allocated_amount(self):
for d in self.get("references"):
if (flt(d.allocated_amount))> 0:
if flt(d.allocated_amount) > flt(d.outstanding_amount):
frappe.throw(_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx))
def delink_advance_entry_references(self):
for reference in self.references:
if reference.reference_doctype in ("Sales Invoice", "Purchase Invoice"):
@ -149,8 +166,6 @@ class PaymentEntry(AccountsController):
if self.paid_from and not self.source_exchange_rate:
if self.paid_from_account_currency == self.company_currency:
self.source_exchange_rate = 1
elif self.payment_type in ("Pay", "Internal Transfer"):
self.source_exchange_rate = get_average_exchange_rate(self.paid_from)
else:
self.source_exchange_rate = get_exchange_rate(self.paid_from_account_currency,
self.company_currency, self.posting_date)

View File

@ -112,6 +112,34 @@ class TestPaymentEntry(unittest.TestCase):
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, 80)
def test_payment_entry_retrieves_last_exchange_rate(self):
from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records, save_new_records
test_records = test_records
save_new_records(test_records)
pe = frappe.new_doc("Payment Entry")
pe.payment_type = "Pay"
pe.company = "_Test Company"
pe.posting_date = "2016-01-10"
pe.paid_from = "_Test Bank USD - _TC"
pe.paid_to = "_Test Bank - _TC"
pe.paid_amount = 100
pe.reference_no = "3"
pe.reference_date = "2016-01-10"
pe.party_type = "Supplier"
pe.party = "_Test Supplier USD"
pe.setup_party_account_field()
pe.set_missing_values()
pe.set_exchange_rate()
pe.set_amounts()
self.assertEqual(
pe.source_exchange_rate, 65.1,
"{0} is not equal to {1}".format(pe.source_exchange_rate, 65.1)
)
def test_internal_transfer_usd_to_inr(self):
pe = frappe.new_doc("Payment Entry")

View File

@ -14,7 +14,7 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
this.frm.set_query('receivable_payable_account', function() {
if(!me.frm.doc.company || !me.frm.doc.party_type) {
msgprint(__("Please select Company and Party Type first"));
frappe.msgprint(__("Please select Company and Party Type first"));
} else {
return{
filters: {
@ -29,7 +29,7 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
this.frm.set_query('bank_cash_account', function() {
if(!me.frm.doc.company) {
msgprint(__("Please select Company first"));
frappe.msgprint(__("Please select Company first"));
} else {
return{
filters:[
@ -96,10 +96,11 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
},
set_invoice_options: function() {
var me = this;
var invoices = [];
$.each(me.frm.doc.invoices || [], function(i, row) {
if (row.invoice_number && !inList(invoices, row.invoice_number))
if (row.invoice_number && !in_list(invoices, row.invoice_number))
invoices.push(row.invoice_type + " | " + row.invoice_number);
});
@ -108,7 +109,7 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
me.frm.doc.name).options = "\n" + invoices.join("\n");
$.each(me.frm.doc.payments || [], function(i, p) {
if(!inList(invoices, cstr(p.invoice_number))) p.invoice_number = null;
if(!in_list(invoices, cstr(p.invoice_number))) p.invoice_number = null;
});
}

View File

@ -87,8 +87,8 @@ class TestPaymentRequest(unittest.TestCase):
expected_gle = dict((d[0], d) for d in [
["_Test Receivable USD - _TC", 0, 5000, si_usd.name],
[pr.payment_account, 6000.0, 0, None],
["_Test Exchange Gain/Loss - _TC", 0, 1000, None]
[pr.payment_account, 6290.0, 0, None],
["_Test Exchange Gain/Loss - _TC", 0, 1290, None]
])
gl_entries = frappe.db.sql("""select account, debit, credit, against_voucher

View File

@ -3,7 +3,7 @@
frappe.ui.form.on('Period Closing Voucher', {
onload: function(frm) {
if (!frm.doc.transaction_date) frm.doc.transaction_date = dateutil.obj_to_str(new Date());
if (!frm.doc.transaction_date) frm.doc.transaction_date = frappe.datetime.obj_to_str(new Date());
},
setup: function(frm) {

View File

@ -26,6 +26,23 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) {
});
});
frappe.ui.form.on('POS Profile', {
refresh: function(frm) {
if(frm.doc.company) {
frm.trigger("toggle_display_account_head");
}
},
company: function(frm) {
frm.trigger("toggle_display_account_head");
},
toggle_display_account_head: function(frm) {
frm.toggle_display('expense_account',
erpnext.is_perpetual_inventory_enabled(frm.doc.company));
}
})
// Income Account
// --------------------------------
cur_frm.fields_dict['income_account'].get_query = function(doc,cdt,cdn) {
@ -35,8 +52,8 @@ cur_frm.fields_dict['income_account'].get_query = function(doc,cdt,cdn) {
'company': doc.company,
'account_type': "Income Account"
}
}
}
};
};
// Cost Center
@ -47,8 +64,8 @@ cur_frm.fields_dict['cost_center'].get_query = function(doc,cdt,cdn) {
'company': doc.company,
'is_group': 0
}
}
}
};
};
// Expense Account
@ -60,8 +77,8 @@ cur_frm.fields_dict["expense_account"].get_query = function(doc) {
"company": doc.company,
"is_group": 0
}
}
}
};
};
// ------------------ Get Print Heading ------------------------------------
cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn) {
@ -69,13 +86,13 @@ cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn)
filters:[
['Print Heading', 'docstatus', '!=', 2]
]
}
}
};
};
cur_frm.fields_dict.user.get_query = function(doc,cdt,cdn) {
return{ query:"frappe.core.doctype.user.user.user_query"}
}
return{ query:"frappe.core.doctype.user.user.user_query"};
};
cur_frm.fields_dict.write_off_account.get_query = function(doc) {
return{
@ -84,16 +101,16 @@ cur_frm.fields_dict.write_off_account.get_query = function(doc) {
'is_group': 0,
'company': doc.company
}
}
}
};
};
// Write off cost center
//-----------------------
// -----------------------
cur_frm.fields_dict.write_off_cost_center.get_query = function(doc) {
return{
filters:{
'is_group': 0,
'company': doc.company
}
}
}
};
};

View File

@ -40,7 +40,7 @@
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"set_only_once": 1,
"unique": 0
},
{
@ -1132,7 +1132,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)",
"depends_on": "",
"fieldname": "expense_account",
"fieldtype": "Link",
"hidden": 0,
@ -1201,7 +1201,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-06-13 14:29:06.317317",
"modified": "2017-06-16 17:04:33.165676",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Profile",

View File

@ -2,63 +2,65 @@
// License: GNU General Public License v3. See license.txt
frappe.ui.form.on("Pricing Rule", "refresh", function(frm) {
var help_content = ['<table class="table table-bordered" style="background-color: #f9f9f9;">',
'<tr><td>',
'<h4><i class="fa fa-hand-right"></i> ',
__('Notes'),
':</h4>',
'<ul>',
'<li>',
__("Pricing Rule is made to overwrite Price List / define discount percentage, based on some criteria."),
'</li>',
'<li>',
__("If selected Pricing Rule is made for 'Price', it will overwrite Price List. Pricing Rule price is the final price, so no further discount should be applied. Hence, in transactions like Sales Order, Purchase Order etc, it will be fetched in 'Rate' field, rather than 'Price List Rate' field."),
'</li>',
'<li>',
__('Discount Percentage can be applied either against a Price List or for all Price List.'),
'</li>',
'<li>',
__('To not apply Pricing Rule in a particular transaction, all applicable Pricing Rules should be disabled.'),
'</li>',
'</ul>',
'</td></tr>',
'<tr><td>',
'<h4><i class="fa fa-question-sign"></i> ',
__('How Pricing Rule is applied?'),
'</h4>',
'<ol>',
'<li>',
__("Pricing Rule is first selected based on 'Apply On' field, which can be Item, Item Group or Brand."),
'</li>',
'<li>',
__("Then Pricing Rules are filtered out based on Customer, Customer Group, Territory, Supplier, Supplier Type, Campaign, Sales Partner etc."),
'</li>',
'<li>',
__('Pricing Rules are further filtered based on quantity.'),
'</li>',
'<li>',
__('If two or more Pricing Rules are found based on the above conditions, Priority is applied. Priority is a number between 0 to 20 while default value is zero (blank). Higher number means it will take precedence if there are multiple Pricing Rules with same conditions.'),
'</li>',
'<li>',
__('Even if there are multiple Pricing Rules with highest priority, then following internal priorities are applied:'),
'<ul>',
'<li>',
__('Item Code > Item Group > Brand'),
'</li>',
'<li>',
__('Customer > Customer Group > Territory'),
'</li>',
'<li>',
__('Supplier > Supplier Type'),
'</li>',
'</ul>',
'</li>',
'<li>',
__('If multiple Pricing Rules continue to prevail, users are asked to set Priority manually to resolve conflict.'),
'</li>',
'</ol>',
'</td></tr>',
'</table>'].join("\n");
var help_content =
`<table class="table table-bordered" style="background-color: #f9f9f9;">
<tr><td>
<h4>
<i class="fa fa-hand-right"></i>
${__('Notes')}
</h4>
<ul>
<li>
${__("Pricing Rule is made to overwrite Price List / define discount percentage, based on some criteria.")}
</li>
<li>
${__("If selected Pricing Rule is made for 'Price', it will overwrite Price List. Pricing Rule price is the final price, so no further discount should be applied. Hence, in transactions like Sales Order, Purchase Order etc, it will be fetched in 'Rate' field, rather than 'Price List Rate' field.")}
</li>
<li>
${__('Discount Percentage can be applied either against a Price List or for all Price List.')}
</li>
<li>
${__('To not apply Pricing Rule in a particular transaction, all applicable Pricing Rules should be disabled.')}
</li>
</ul>
</td></tr>
<tr><td>
<h4><i class="fa fa-question-sign"></i>
${__('How Pricing Rule is applied?')}
</h4>
<ol>
<li>
${__("Pricing Rule is first selected based on 'Apply On' field, which can be Item, Item Group or Brand.")}
</li>
<li>
${__("Then Pricing Rules are filtered out based on Customer, Customer Group, Territory, Supplier, Supplier Type, Campaign, Sales Partner etc.")}
</li>
<li>
${__('Pricing Rules are further filtered based on quantity.')}
</li>
<li>
${__('If two or more Pricing Rules are found based on the above conditions, Priority is applied. Priority is a number between 0 to 20 while default value is zero (blank). Higher number means it will take precedence if there are multiple Pricing Rules with same conditions.')}
</li>
<li>
${__('Even if there are multiple Pricing Rules with highest priority, then following internal priorities are applied:')}
<ul>
<li>
${__('Item Code > Item Group > Brand')}
</li>
<li>
${__('Customer > Customer Group > Territory')}
</li>
<li>
${__('Supplier > Supplier Type')}
</li>
</ul>
</li>
<li>
${__('If multiple Pricing Rules continue to prevail, users are asked to set Priority manually to resolve conflict.')}
</li>
</ol>
</td></tr>
</table>`;
set_field_options("pricing_rule_help", help_content);

View File

@ -101,8 +101,8 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
account: this.frm.doc.credit_to,
price_list: this.frm.doc.buying_price_list,
}, function() {
me.apply_pricing_rule();
})
me.apply_pricing_rule();
})
},
credit_to: function() {
@ -130,7 +130,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
if(cint(this.frm.doc.is_paid)) {
if(!this.frm.doc.company) {
this.frm.set_value("is_paid", 0)
msgprint(__("Please specify Company to proceed"));
frappe.msgprint(__("Please specify Company to proceed"));
}
}
this.calculate_outstanding_amount();
@ -195,19 +195,19 @@ cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);
// Hide Fields
// ------------
function hide_fields(doc) {
parent_fields = ['due_date', 'is_opening', 'advances_section', 'from_date', 'to_date'];
var parent_fields = ['due_date', 'is_opening', 'advances_section', 'from_date', 'to_date'];
if(cint(doc.is_paid) == 1) {
hide_field(parent_fields);
} else {
for (i in parent_fields) {
for (var i in parent_fields) {
var docfield = frappe.meta.docfield_map[doc.doctype][parent_fields[i]];
if(!docfield.hidden) unhide_field(parent_fields[i]);
}
}
item_fields_stock = ['warehouse_section', 'received_qty', 'rejected_qty'];
var item_fields_stock = ['warehouse_section', 'received_qty', 'rejected_qty'];
cur_frm.fields_dict['items'].grid.set_column_disp(item_fields_stock,
(cint(doc.update_stock)==1 || cint(doc.is_return)==1 ? true : false));

View File

@ -11,7 +11,7 @@ from erpnext.controllers.buying_controller import BuyingController
from erpnext.accounts.party import get_party_account, get_due_date
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billed_amount_based_on_po
from erpnext.controllers.stock_controller import get_warehouse_account
from erpnext.stock import get_warehouse_account_map
from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, delete_gl_entries
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
from erpnext.buying.utils import check_for_closed_status
@ -59,6 +59,7 @@ class PurchaseInvoice(BuyingController):
self.check_for_closed_status()
self.validate_with_previous_doc()
self.validate_uom_is_integer("uom", "qty")
self.validate_uom_is_integer("stock_uom", "stock_qty")
self.set_expense_account(for_validate=True)
self.set_against_expense_account()
self.validate_write_off_account()
@ -163,7 +164,7 @@ class PurchaseInvoice(BuyingController):
frappe.msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True)
def set_expense_account(self, for_validate=False):
auto_accounting_for_stock = cint(frappe.defaults.get_global_default("auto_accounting_for_stock"))
auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
if auto_accounting_for_stock:
stock_not_billed_account = self.get_company_default("stock_received_but_not_billed")
@ -172,7 +173,7 @@ class PurchaseInvoice(BuyingController):
if self.update_stock:
self.validate_item_code()
self.validate_warehouse()
warehouse_account = get_warehouse_account()
warehouse_account = get_warehouse_account_map()
for item in self.get("items"):
# in case of auto inventory accounting,
@ -185,7 +186,7 @@ class PurchaseInvoice(BuyingController):
not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")):
if self.update_stock:
item.expense_account = warehouse_account[item.warehouse]["name"]
item.expense_account = warehouse_account[item.warehouse]["account"]
else:
item.expense_account = stock_not_billed_account
@ -204,14 +205,14 @@ class PurchaseInvoice(BuyingController):
if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes':
for d in self.get('items'):
if not d.purchase_order:
throw(_("Purchase Order number required for Item {0}").format(d.item_code))
throw(_("As per the Buying Settings if Purchase Order Required == 'YES', then for creating Purchase Invoice, user need to create Purchase Order first for item {0}").format(d.item_code))
def pr_required(self):
stock_items = self.get_stock_items()
if frappe.db.get_value("Buying Settings", None, "pr_required") == 'Yes':
for d in self.get('items'):
if not d.purchase_receipt and d.item_code in stock_items:
throw(_("Purchase Receipt number required for Item {0}").format(d.item_code))
throw(_("As per the Buying Settings if Purchase Reciept Required == 'YES', then for creating Purchase Invoice, user need to create Purchase Receipt first for item {0}").format(d.item_code))
def validate_write_off_account(self):
if self.write_off_amount and not self.write_off_account:
@ -334,9 +335,7 @@ class PurchaseInvoice(BuyingController):
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
def get_gl_entries(self, warehouse_account=None):
self.auto_accounting_for_stock = \
cint(frappe.defaults.get_global_default("auto_accounting_for_stock"))
self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
self.stock_received_but_not_billed = self.get_company_default("stock_received_but_not_billed")
self.expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
self.negative_expense_to_be_booked = 0.0
@ -377,7 +376,7 @@ class PurchaseInvoice(BuyingController):
# item gl entries
stock_items = self.get_stock_items()
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
warehouse_account = get_warehouse_account()
warehouse_account = get_warehouse_account_map()
for item in self.get("items"):
if flt(item.base_net_amount):

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals
import unittest
import frappe
import frappe, erpnext
import frappe.model
from frappe.utils import cint, flt, today, nowdate
import frappe.defaults
@ -12,6 +12,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_per
test_records as pr_test_records
from erpnext.exceptions import InvalidCurrency
from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction
from erpnext.accounts.doctype.account.test_account import get_inventory_account
test_dependencies = ["Item", "Cost Center"]
test_ignore = ["Serial No"]
@ -24,11 +25,10 @@ class TestPurchaseInvoice(unittest.TestCase):
def tearDown(self):
unlink_payment_on_cancel_of_invoice(0)
def test_gl_entries_without_auto_accounting_for_stock(self):
set_perpetual_inventory(0)
self.assertTrue(not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")))
def test_gl_entries_without_perpetual_inventory(self):
wrapper = frappe.copy_doc(test_records[0])
set_perpetual_inventory(0, wrapper.company)
self.assertTrue(not cint(erpnext.is_perpetual_inventory_enabled(wrapper.company)))
wrapper.insert()
wrapper.submit()
wrapper.load_from_db()
@ -50,17 +50,16 @@ class TestPurchaseInvoice(unittest.TestCase):
for d in gl_entries:
self.assertEqual([d.debit, d.credit], expected_gl_entries.get(d.account))
def test_gl_entries_with_auto_accounting_for_stock(self):
set_perpetual_inventory(1)
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1)
def test_gl_entries_with_perpetual_inventory(self):
pi = frappe.copy_doc(test_records[1])
set_perpetual_inventory(1, pi.company)
self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pi.company)), 1)
pi.insert()
pi.submit()
self.check_gle_for_pi(pi.name)
set_perpetual_inventory(0)
set_perpetual_inventory(0, pi.company)
def test_payment_entry_unlink_against_purchase_invoice(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
@ -83,11 +82,10 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertRaises(frappe.LinkExistsError, pi_doc.cancel)
def test_gl_entries_with_auto_accounting_for_stock_against_pr(self):
set_perpetual_inventory(1)
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1)
def test_gl_entries_with_perpetual_inventory_against_pr(self):
pr = frappe.copy_doc(pr_test_records[0])
set_perpetual_inventory(1, pr.company)
self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pr.company)), 1)
pr.submit()
pi = frappe.copy_doc(test_records[1])
@ -98,7 +96,7 @@ class TestPurchaseInvoice(unittest.TestCase):
self.check_gle_for_pi(pi.name)
set_perpetual_inventory(0)
set_perpetual_inventory(0, pr.company)
def check_gle_for_pi(self, pi):
gl_entries = frappe.db.sql("""select account, debit, credit
@ -132,10 +130,9 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertRaises(frappe.CannotChangeConstantError, pi.save)
def test_gl_entries_with_aia_for_non_stock_items(self):
set_perpetual_inventory()
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1)
pi = frappe.copy_doc(test_records[1])
set_perpetual_inventory(1, pi.company)
self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pi.company)), 1)
pi.get("items")[0].item_code = "_Test Non Stock Item"
pi.get("items")[0].expense_account = "_Test Account Cost for Goods Sold - _TC"
pi.get("taxes").pop(0)
@ -158,7 +155,7 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertEquals(expected_values[i][0], gle.account)
self.assertEquals(expected_values[i][1], gle.debit)
self.assertEquals(expected_values[i][2], gle.credit)
set_perpetual_inventory(0)
set_perpetual_inventory(0, pi.company)
def test_purchase_invoice_calculation(self):
pi = frappe.copy_doc(test_records[0])
@ -370,10 +367,11 @@ class TestPurchaseInvoice(unittest.TestCase):
order by account asc""", pi.name, as_dict=1)
self.assertTrue(gl_entries)
stock_in_hand_account = get_inventory_account(pi.company, pi.get("items")[0].warehouse)
expected_gl_entries = dict((d[0], d) for d in [
[pi.credit_to, 0.0, 250.0],
[pi.items[0].warehouse, 250.0, 0.0]
[stock_in_hand_account, 250.0, 0.0]
])
for i, gle in enumerate(gl_entries):
@ -390,12 +388,13 @@ class TestPurchaseInvoice(unittest.TestCase):
sum(credit) as credit, debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
group by account, voucher_no order by account asc;""", pi.name, as_dict=1)
stock_in_hand_account = get_inventory_account(pi.company, pi.get("items")[0].warehouse)
self.assertTrue(gl_entries)
expected_gl_entries = dict((d[0], d) for d in [
[pi.credit_to, 250.0, 250.0],
[pi.items[0].warehouse, 250.0, 0.0],
[stock_in_hand_account, 250.0, 0.0],
["Cash - _TC", 0.0, 250.0]
])

View File

@ -9,11 +9,11 @@ frappe.ui.form.on("Purchase Taxes and Charges", "add_deduct_tax", function(doc,
var d = locals[cdt][cdn];
if(!d.category && d.add_deduct_tax) {
msgprint(__("Please select Category first"));
frappe.msgprint(__("Please select Category first"));
d.add_deduct_tax = '';
}
else if(d.category != 'Total' && d.add_deduct_tax == 'Deduct') {
msgprint(__("Cannot deduct when category is for 'Valuation' or 'Valuation and Total'"));
frappe.msgprint(__("Cannot deduct when category is for 'Valuation' or 'Valuation and Total'"));
d.add_deduct_tax = '';
}
refresh_field('add_deduct_tax', d.name, 'taxes');
@ -23,7 +23,7 @@ frappe.ui.form.on("Purchase Taxes and Charges", "category", function(doc, cdt, c
var d = locals[cdt][cdn];
if (d.category != 'Total' && d.add_deduct_tax == 'Deduct') {
msgprint(__("Cannot deduct when category is for 'Valuation' or 'Vaulation and Total'"));
frappe.msgprint(__("Cannot deduct when category is for 'Valuation' or 'Vaulation and Total'"));
d.add_deduct_tax = '';
}
refresh_field('add_deduct_tax', d.name, 'taxes');

View File

@ -175,8 +175,8 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
account: this.frm.doc.debit_to,
price_list: this.frm.doc.selling_price_list,
}, function() {
me.apply_pricing_rule();
})
me.apply_pricing_rule();
})
},
debit_to: function() {
@ -267,7 +267,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
if(this.frm.doc.is_pos) {
if(!this.frm.doc.company) {
this.frm.set_value("is_pos", 0);
msgprint(__("Please specify Company to proceed"));
frappe.msgprint(__("Please specify Company to proceed"));
} else {
var me = this;
return this.frm.call({
@ -312,19 +312,20 @@ $.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_
// Hide Fields
// ------------
cur_frm.cscript.hide_fields = function(doc) {
parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances',
var parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances',
'advances', 'sales_partner', 'commission_rate', 'total_commission', 'advances', 'from_date', 'to_date'];
if(cint(doc.is_pos) == 1) {
hide_field(parent_fields);
} else {
for (i in parent_fields) {
for (var i in parent_fields) {
var docfield = frappe.meta.docfield_map[doc.doctype][parent_fields[i]];
if(!docfield.hidden) unhide_field(parent_fields[i]);
}
}
item_fields_stock = ['serial_no', 'batch_no', 'actual_qty', 'expense_account', 'warehouse', 'expense_account', 'warehouse']
var item_fields_stock = ['batch_no', 'actual_batch_qty', 'actual_qty', 'expense_account',
'warehouse', 'expense_account', 'quality_inspection']
cur_frm.fields_dict['items'].grid.set_column_disp(item_fields_stock,
(cint(doc.update_stock)==1 || cint(doc.is_return)==1 ? true : false));
@ -399,19 +400,6 @@ cur_frm.set_query("income_account", "items", function(doc) {
}
});
// expense account
if (sys_defaults.auto_accounting_for_stock) {
cur_frm.fields_dict['items'].grid.get_field('expense_account').get_query = function(doc) {
return {
filters: {
'report_type': 'Profit and Loss',
'company': doc.company,
"is_group": 0
}
}
}
}
// Cost Center in Details Table
// -----------------------------
@ -442,11 +430,12 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
})
if(cur_frm.doc.is_pos) {
cur_frm.msgbox = frappe.msgprint(format('<a class="btn btn-primary" \
onclick="cur_frm.print_preview.printit(true)" style="margin-right: 5px;">{0}</a>\
<a class="btn btn-default" href="javascript:frappe.new_doc(cur_frm.doctype);">{1}</a>', [
__('Print'), __('New')
]));
cur_frm.msgbox = frappe.msgprint(
`<a class="btn btn-primary" onclick="cur_frm.print_preview.printit(true)" style="margin-right: 5px;">
${__('Print')}</a>
<a class="btn btn-default" href="javascript:frappe.new_doc(cur_frm.doctype);">
${__('New')}</a>`
);
} else if(cint(frappe.boot.notification_settings.sales_invoice)) {
cur_frm.email_doc(frappe.boot.notification_settings.sales_invoice_message);
@ -500,6 +489,21 @@ frappe.ui.form.on('Sales Invoice', {
filters: {'project': doc.project}
}
}
// expense account
frm.fields_dict['items'].grid.get_field('expense_account').get_query = function(doc) {
if (erpnext.is_perpetual_inventory_enabled(doc.company)) {
return {
filters: {
'report_type': 'Profit and Loss',
'company': doc.company,
"is_group": 0
}
}
}
}
},
project: function(frm){

View File

@ -324,6 +324,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "pos_profile",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "POS Profile",
"length": 0,
"no_copy": 0,
"options": "POS Profile",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -4565,7 +4596,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-06-13 14:29:14.696232",
"modified": "2017-06-16 17:07:55.483734",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
import frappe, erpnext
import frappe.defaults
from frappe.utils import cint, flt
from frappe import _, msgprint, throw
@ -57,7 +57,8 @@ class SalesInvoice(SellingController):
self.so_dn_required()
self.validate_proj_cust()
self.validate_with_previous_doc()
self.validate_uom_is_integer("stock_uom", "qty")
self.validate_uom_is_integer("stock_uom", "stock_qty")
self.validate_uom_is_integer("uom", "qty")
self.check_close_sales_order("sales_order")
self.validate_debit_to_acc()
self.clear_unallocated_advances("Sales Invoice Advance", "advances")
@ -88,6 +89,8 @@ class SalesInvoice(SellingController):
self.validate_c_form()
self.validate_time_sheets_are_submitted()
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount", "items")
if not self.is_return:
self.validate_serial_numbers()
self.update_packing_list()
self.set_billing_hours_and_amount()
self.update_timesheet_billing_for_project()
@ -125,6 +128,7 @@ class SalesInvoice(SellingController):
if not self.is_return:
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
self.check_credit_limit()
self.update_serial_no()
if not cint(self.is_pos) == 1 and not self.is_return:
self.update_against_document_in_jv()
@ -155,6 +159,7 @@ class SalesInvoice(SellingController):
if not self.is_return:
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
self.update_serial_no(in_cancel=True)
self.validate_c_form_on_cancel()
@ -554,6 +559,8 @@ class SalesInvoice(SellingController):
throw(_("Delivery Note {0} is not submitted").format(d.delivery_note))
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
if not self.grand_total:
return
@ -575,11 +582,11 @@ class SalesInvoice(SellingController):
self.doctype, self.return_against if cint(self.is_return) else self.name)
if repost_future_gle and cint(self.update_stock) \
and cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
and cint(auto_accounting_for_stock):
items, warehouses = self.get_items_and_warehouses()
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items)
elif self.docstatus == 2 and cint(self.update_stock) \
and cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
and cint(auto_accounting_for_stock):
from erpnext.accounts.general_ledger import delete_gl_entries
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
@ -667,8 +674,8 @@ class SalesInvoice(SellingController):
)
# expense account gl entries
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) \
and cint(self.update_stock):
if cint(self.update_stock) and \
erpnext.is_perpetual_inventory_enabled(self.company):
gl_entries += super(SalesInvoice, self).get_gl_entries()
def make_pos_gl_entries(self, gl_entries):
@ -781,6 +788,61 @@ class SalesInvoice(SellingController):
self.due_date = None
def update_serial_no(self, in_cancel=False):
""" update Sales Invoice refrence in Serial No """
for item in self.items:
if not item.serial_no:
continue
serial_nos = ["'%s'"%serial_no for serial_no in item.serial_no.split("\n")]
frappe.db.sql(""" update `tabSerial No` set sales_invoice='{invoice}'
where name in ({serial_nos})""".format(
invoice='' if in_cancel else self.name,
serial_nos=",".join(serial_nos)
)
)
def validate_serial_numbers(self):
"""
validate serial number agains Delivery Note and Sales Invoice
"""
self.validate_serial_against_delivery_note()
self.validate_serial_against_sales_invoice()
def validate_serial_against_delivery_note(self):
"""
validate if the serial numbers in Sales Invoice Items are same as in
Delivery Note Item
"""
for item in self.items:
if not item.delivery_note or not item.dn_detail:
continue
serial_nos = frappe.db.get_value("Delivery Note Item", item.dn_detail, "serial_no") or ""
dn_serial_nos = set(serial_nos.split("\n"))
serial_nos = item.serial_no or ""
si_serial_nos = set(serial_nos.split("\n"))
if si_serial_nos - dn_serial_nos:
frappe.throw(_("Serial Numbers in row {0} does not match with Delivery Note".format(item.idx)))
def validate_serial_against_sales_invoice(self):
""" check if serial number is already used in other sales invoice """
for item in self.items:
if not item.serial_no:
continue
for serial_no in item.serial_no.split("\n"):
sales_invoice = frappe.db.get_value("Serial No", serial_no, "sales_invoice")
if sales_invoice and self.name != sales_invoice:
frappe.throw(_("Serial Number: {0} is already referenced in Sales Invoice: {1}".format(
serial_no, sales_invoice
)))
def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context
list_context = get_list_context(context)

View File

@ -12,6 +12,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_per
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError
from frappe.model.naming import make_autoname
from erpnext.accounts.doctype.account.test_account import get_inventory_account
class TestSalesInvoice(unittest.TestCase):
def make(self):
@ -487,8 +488,8 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEquals(frappe.db.get_value("Sales Invoice", w.name, "outstanding_amount"), 561.8)
def test_sales_invoice_gl_entry_without_perpetual_inventory(self):
set_perpetual_inventory(0)
si = frappe.copy_doc(test_records[1])
set_perpetual_inventory(0, si.company)
si.insert()
si.submit()
@ -595,7 +596,7 @@ class TestSalesInvoice(unittest.TestCase):
order by account asc, debit asc""", si.name, as_dict=1)
self.assertTrue(gl_entries)
stock_in_hand = frappe.db.get_value("Account", {"warehouse": "_Test Warehouse - _TC"})
stock_in_hand = get_inventory_account('_Test Company')
expected_gl_entries = sorted([
[si.debit_to, 630.0, 0.0],
@ -616,6 +617,7 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEquals(expected_gl_entries[i][2], gle.credit)
si.cancel()
frappe.delete_doc('Sales Invoice', si.name)
gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
@ -750,6 +752,12 @@ class TestSalesInvoice(unittest.TestCase):
self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"))
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0],
"delivery_document_no"), si.name)
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "sales_invoice"),
si.name)
# check if the serial number is already linked with any other Sales Invoice
_si = frappe.copy_doc(si.as_dict())
self.assertRaises(frappe.ValidationError, _si.insert)
return si
@ -763,6 +771,7 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"), "_Test Warehouse - _TC")
self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0],
"delivery_document_no"))
self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "sales_invoice"))
def test_serialize_status(self):
serial_no = frappe.get_doc({
@ -781,6 +790,27 @@ class TestSalesInvoice(unittest.TestCase):
self.assertRaises(SerialNoWarehouseError, si.submit)
def test_serial_numbers_against_delivery_note(self):
"""
check if the sales invoice item serial numbers and the delivery note items
serial numbers are same
"""
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
se = make_serialized_item()
serial_nos = get_serial_nos(se.get("items")[0].serial_no)
dn = create_delivery_note(item=se.get("items")[0].item_code, serial_no=serial_nos[0])
dn.submit()
si = make_sales_invoice(dn.name)
si.save()
self.assertEquals(si.get("items")[0].serial_no, dn.get("items")[0].serial_no)
def test_invoice_due_date_against_customers_credit_days(self):
# set customer's credit days
frappe.db.set_value("Customer", "_Test Customer", "credit_days_based_on", "Fixed Days")
@ -822,11 +852,11 @@ class TestSalesInvoice(unittest.TestCase):
["incoming_rate", "stock_value_difference"])
self.assertEquals(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3)))
stock_in_hand_account = get_inventory_account('_Test Company', si1.items[0].warehouse)
# Check gl entry
gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice",
"voucher_no": si1.name, "account": "_Test Warehouse - _TC"}, "debit")
"voucher_no": si1.name, "account": stock_in_hand_account}, "debit")
self.assertEquals(gle_warehouse_amount, stock_value_difference)

View File

@ -7,7 +7,7 @@ import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import cstr, cint
from frappe.geo.doctype.address.address import get_default_address
from frappe.contacts.doctype.address.address import get_default_address
class IncorrectCustomerGroup(frappe.ValidationError): pass
class IncorrectSupplierType(frappe.ValidationError): pass

View File

@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
import frappe, erpnext
from frappe.utils import flt, cstr, cint
from frappe import _
from frappe.model.meta import get_field_precision
@ -80,7 +80,7 @@ def check_if_in_list(gle, gl_map):
def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
if not from_repost:
validate_account_for_auto_accounting_for_stock(gl_map)
validate_account_for_perpetual_inventory(gl_map)
round_off_debit_credit(gl_map)
@ -100,11 +100,11 @@ def make_entry(args, adv_adj, update_outstanding, from_repost=False):
gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost)
gle.submit()
def validate_account_for_auto_accounting_for_stock(gl_map):
if cint(frappe.db.get_single_value("Accounts Settings", "auto_accounting_for_stock")) \
def validate_account_for_perpetual_inventory(gl_map):
if cint(erpnext.is_perpetual_inventory_enabled(gl_map[0].company)) \
and gl_map[0].voucher_type=="Journal Entry":
aii_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
where account_type = 'Stock' and (warehouse != '' and warehouse is not null) and is_group=0""")]
where account_type = 'Stock' and is_group=0""")]
for entry in gl_map:
if entry.account in aii_accounts:

View File

@ -114,7 +114,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
email_prompt: function() {
var me = this;
fields = [{label:__("To"), fieldtype:"Data", reqd: 0, fieldname:"recipients",length:524288},
var fields = [{label:__("To"), fieldtype:"Data", reqd: 0, fieldname:"recipients",length:524288},
{fieldtype: "Section Break", collapsible: 1, label: "CC & Standard Reply"},
{fieldtype: "Section Break"},
{label:__("Subject"), fieldtype:"Data", reqd: 1,
@ -399,7 +399,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.make_item_list();
this.make_discount_field()
},
make_control: function() {
this.frm = {}
this.frm.doc = this.doc
@ -545,7 +545,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
me.toggle_totals_area();
});
},
bind_numeric_keypad: function() {
var me = this;
$(this.numeric_keypad).find('.pos-operation').on('click', function(){
@ -574,7 +574,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
me.selected_field.closest('.pos-list-row').addClass('active');
}
})
$(this.numeric_keypad).find('.numeric-del').click(function(){
me.selected_field = $(me.wrapper).find('.selected-item').find('.' + me.numeric_id)
me.numeric_val = cstr(flt(me.selected_field.val())).slice(0, -1);
@ -582,7 +582,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
me.selected_field.trigger("change")
// me.render_selected_item()
})
$(this.numeric_keypad).find('.pos-pay').click(function(){
me.validate();
me.update_paid_amount_status(true);
@ -604,7 +604,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
var html = "";
if(this.si_docs.length) {
this.si_docs.forEach(function (data, i) {
for (key in data) {
for (var key in data) {
html += frappe.render_template("pos_invoice_list", {
sr: i + 1,
name: key,
@ -984,7 +984,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
make_item_list: function () {
var me = this;
if (!this.price_list) {
msgprint(__("Price List not found or disabled"));
frappe.msgprint(__("Price List not found or disabled"));
return;
}
@ -1007,7 +1007,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
})).tooltip().appendTo($wrap);
}
});
$wrap.append(`
<div class="image-view-item btn-more text-muted text-center">
<div class="image-view-body">
@ -1089,7 +1089,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
});
}
},
bind_items_event: function() {
var me = this;
$(this.wrapper).on('click', '.pos-bill-item', function() {
@ -1126,7 +1126,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) - 1;
me.update_qty(item_code, qty)
})
$(this.wrapper).on("change", ".pos-item-disc", function () {
var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code");
var discount = $(this).val();
@ -1252,7 +1252,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
remove_zero_qty_item: function () {
var me = this;
idx = 0
var idx = 0;
this.items = []
$.each(this.frm.doc["items"] || [], function (i, d) {
if (!in_list(me.remove_item, d.idx)) {
@ -1270,7 +1270,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.wrapper.find('input.discount-percentage').on("change", function () {
me.frm.doc.additional_discount_percentage = flt($(this).val(), precision("additional_discount_percentage"));
total = me.frm.doc.grand_total
var total = me.frm.doc.grand_total
if (me.frm.doc.apply_discount_on == 'Net Total') {
total = me.frm.doc.net_total
@ -1461,7 +1461,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
if (this.frm.doc.docstatus == 1) {
this.page.set_secondary_action(__("Print"), function () {
html = frappe.render(me.print_template_data, me.frm.doc)
var html = frappe.render(me.print_template_data, me.frm.doc)
me.print_document(html)
})
this.page.add_menu_item(__("Email"), function () {
@ -1484,19 +1484,18 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
print_dialog: function () {
var me = this;
this.msgprint = frappe.msgprint(format('<a class="btn btn-primary print_doc" \
style="margin-right: 5px;">{0}</a>\
<a class="btn btn-default new_doc">{1}</a>', [
__('Print'), __('New')
]));
this.frappe.msgprint = frappe.msgprint(
`<a class="btn btn-primary print_doc"
style="margin-right: 5px;">${__('Print')}</a>
<a class="btn btn-default new_doc">${__('New')}</a>`);
$('.print_doc').click(function () {
html = frappe.render(me.print_template_data, me.frm.doc)
var html = frappe.render(me.print_template_data, me.frm.doc)
me.print_document(html)
})
$('.new_doc').click(function () {
me.msgprint.hide()
me.frappe.msgprint.hide()
me.make_new_cart()
})
},
@ -1525,9 +1524,9 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
//Remove the sold serial no from the cache
$.each(this.frm.doc.items, function(index, data) {
sn = data.serial_no.split('\n')
var sn = data.serial_no.split('\n')
if(sn.length) {
serial_no_list = me.serial_no_data[data.item_code]
var serial_no_list = me.serial_no_data[data.item_code]
if(serial_no_list) {
$.each(sn, function(i, serial_no) {
if(in_list(Object.keys(serial_no_list), serial_no)) {
@ -1550,7 +1549,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
toggle_input_field: function () {
var pointer_events = 'inherit'
disabled = this.frm.doc.docstatus == 1 ? true: false;
var disabled = this.frm.doc.docstatus == 1 ? true: false;
$(this.wrapper).find('input').attr("disabled", disabled);
$(this.wrapper).find('select').attr("disabled", disabled);
$(this.wrapper).find('input').attr("disabled", disabled);
@ -1578,6 +1577,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.frm.doc.offline_pos_name = this.name;
this.frm.doc.posting_date = frappe.datetime.get_today();
this.frm.doc.posting_time = frappe.datetime.now_time();
this.frm.doc.pos_profile = this.pos_profile_data['name'];
invoice_data[this.name] = this.frm.doc
this.si_docs.push(invoice_data)
this.update_localstorage();
@ -1590,7 +1590,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
var me = this;
this.si_docs = this.get_doc_from_localstorage();
$.each(this.si_docs, function (index, data) {
for (key in data) {
for (var key in data) {
if (key == me.name) {
me.si_docs[index][key] = me.frm.doc;
me.update_localstorage();
@ -1661,10 +1661,10 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
get_submitted_invoice: function () {
var invoices = [];
var index = 1;
docs = this.get_doc_from_localstorage();
var docs = this.get_doc_from_localstorage();
if (docs) {
invoices = $.map(docs, function (data) {
for (key in data) {
for (var key in data) {
if (data[key].docstatus == 1 && index < 50) {
index++
data[key].docstatus = 0;
@ -1683,7 +1683,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.new_si_docs = [];
if (this.removed_items) {
$.each(this.si_docs, function (index, data) {
for (key in data) {
for (var key in data) {
if (!in_list(me.removed_items, key)) {
me.new_si_docs.push(data);
}
@ -1742,8 +1742,9 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
validate_serial_no: function () {
var me = this;
var item_code = serial_no = '';
for (key in this.item_serial_no) {
var item_code = ''
var serial_no = '';
for (var key in this.item_serial_no) {
item_code = key;
serial_no = me.item_serial_no[key][0];
}
@ -1790,16 +1791,24 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
mandatory_batch_no: function () {
var me = this;
if (this.items[0].has_batch_no && !this.item_batch_no[this.items[0].item_code]) {
frappe.throw(__(repl("Error: Batch no is mandatory for item %(item)s", {
'item': this.items[0].item_code
})))
frappe.prompt([{
'fieldname': 'batch',
'fieldtype': 'Select',
'label': __('Batch No'),
'reqd': 1,
'options': this.batch_no_data[this.items[0].item_code]
}],
function(values){
me.item_batch_no[me.items[0].item_code] = values.batch;
},
__('Select Batch No'))
}
},
apply_pricing_rule: function () {
var me = this;
$.each(this.frm.doc["items"], function (n, item) {
pricing_rule = me.get_pricing_rule(item)
var pricing_rule = me.get_pricing_rule(item)
me.validate_pricing_rule(pricing_rule)
if (pricing_rule.length) {
item.pricing_rule = pricing_rule[0].name;
@ -1815,7 +1824,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
item.pricing_rule = null;
me.apply_pricing_rule_on_item(item)
}
if(item.discount_percentage > 0) {
me.apply_pricing_rule_on_item(item)
}
@ -1859,7 +1868,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
validate_condition: function (data) {
//This method check condition based on applicable for
condition = this.get_mapper_for_pricing_rule(data)[data.applicable_for]
var condition = this.get_mapper_for_pricing_rule(data)[data.applicable_for]
if (in_list(condition[1], condition[0])) {
return true
}
@ -1892,7 +1901,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
}
})
count = 0
var count = 0
$.each(priority_list, function (index, value) {
if (value == priority) {
count++

View File

@ -9,8 +9,8 @@ from frappe import _, msgprint, scrub
from frappe.defaults import get_user_permissions
from frappe.utils import add_days, getdate, formatdate, get_first_day, date_diff, \
add_years, get_timestamp, nowdate, flt
from frappe.geo.doctype.address.address import get_address_display, get_default_address
from frappe.email.doctype.contact.contact import get_contact_details, get_default_contact
from frappe.contacts.doctype.address.address import get_address_display, get_default_address
from frappe.contacts.doctype.contact.contact import get_contact_details, get_default_contact
from erpnext.exceptions import PartyFrozen, InvalidCurrency, PartyDisabled, InvalidAccountCurrency
from erpnext.accounts.utils import get_fiscal_year
from erpnext import get_default_currency

View File

@ -20,13 +20,13 @@ frappe.query_reports["Accounts Payable"] = {
"fieldname":"report_date",
"label": __("As on Date"),
"fieldtype": "Date",
"default": get_today()
"default": frappe.datetime.get_today()
},
{
"fieldname":"ageing_based_on",
"label": __("Ageing Based On"),
"fieldtype": "Select",
"options": 'Posting Date' + NEWLINE + 'Due Date',
"options": 'Posting Date\nDue Date',
"default": "Posting Date"
},
{

View File

@ -20,13 +20,13 @@ frappe.query_reports["Accounts Payable Summary"] = {
"fieldname":"report_date",
"label": __("Date"),
"fieldtype": "Date",
"default": get_today()
"default": frappe.datetime.get_today()
},
{
"fieldname":"ageing_based_on",
"label": __("Ageing Based On"),
"fieldtype": "Select",
"options": 'Posting Date' + NEWLINE + 'Due Date',
"options": 'Posting Date\nDue Date',
"default": "Posting Date"
},
{

View File

@ -1,4 +1,4 @@
{% var letterhead= filters.letter_head || frappe.get_doc(":Company", filters.company).default_letter_head || frappe.defaults.get_default("letter_head"); %}
{% var letterhead= filters.letter_head || (frappe.get_doc(":Company", filters.company) && frappe.get_doc(":Company", filters.company).default_letter_head) || frappe.defaults.get_default("letter_head"); %}
{% if(letterhead) { %}
<div style="margin-bottom: 7px;" class="text-center">
{%= frappe.boot.letter_heads[letterhead].header %}

View File

@ -26,7 +26,7 @@ frappe.query_reports["Accounts Receivable"] = {
"fieldname":"credit_days_based_on",
"label": __("Credit Days Based On"),
"fieldtype": "Select",
"options": "" + NEWLINE + "Fixed Days" + NEWLINE + "Last Day of the Next Month"
"options": "\nFixed Days\nLast Day of the Next Month"
},
{
"fieldtype": "Break",
@ -35,13 +35,13 @@ frappe.query_reports["Accounts Receivable"] = {
"fieldname":"report_date",
"label": __("As on Date"),
"fieldtype": "Date",
"default": get_today()
"default": frappe.datetime.get_today()
},
{
"fieldname":"ageing_based_on",
"label": __("Ageing Based On"),
"fieldtype": "Select",
"options": 'Posting Date' + NEWLINE + 'Due Date',
"options": 'Posting Date\nDue Date',
"default": "Posting Date"
},
{

View File

@ -26,7 +26,7 @@ frappe.query_reports["Accounts Receivable Summary"] = {
"fieldname":"credit_days_based_on",
"label": __("Credit Days Based On"),
"fieldtype": "Select",
"options": "" + NEWLINE + "Fixed Days" + NEWLINE + "Last Day of the Next Month"
"options": "\nFixed Days\nLast Day of the Next Month"
},
{
"fieldtype": "Break",
@ -35,13 +35,13 @@ frappe.query_reports["Accounts Receivable Summary"] = {
"fieldname":"report_date",
"label": __("Date"),
"fieldtype": "Date",
"default": get_today()
"default": frappe.datetime.get_today()
},
{
"fieldname":"ageing_based_on",
"label": __("Ageing Based On"),
"fieldtype": "Select",
"options": 'Posting Date' + NEWLINE + 'Due Date',
"options": 'Posting Date\nDue Date',
"default": "Posting Date"
},
{
@ -70,7 +70,7 @@ frappe.query_reports["Accounts Receivable Summary"] = {
onload: function(report) {
report.page.add_inner_button(__("Accounts Receivable"), function() {
var filters = report.get_values();
frappe.set_route('query-report', 'Accounts Receivable', {company: filters.company});
frappe.set_route('query-report', 'Accounts Receivable', { company: filters.company });
});
}
}

View File

@ -14,7 +14,7 @@ frappe.query_reports["Bank Clearance Summary"] = {
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": get_today()
"default": frappe.datetime.get_today()
},
{
"fieldname":"account",

View File

@ -25,7 +25,7 @@ frappe.query_reports["Bank Reconciliation Statement"] = {
"fieldname":"report_date",
"label": __("Date"),
"fieldtype": "Date",
"default": get_today(),
"default": frappe.datetime.get_today(),
"reqd": 1
},
]

View File

@ -8,7 +8,7 @@ frappe.query_reports["Budget Variance Report"] = {
label: __("Fiscal Year"),
fieldtype: "Link",
options: "Fiscal Year",
default: sys_defaults.fiscal_year,
default: frappe.sys_defaults.fiscal_year,
reqd: 1
},
{

View File

@ -13,7 +13,7 @@
height: 37px;
}
</style>
{% var letterhead= filters.letter_head || frappe.get_doc(":Company", filters.company).default_letter_head || frappe.defaults.get_default("letter_head"); %}
{% var letterhead= filters.letter_head || (frappe.get_doc(":Company", filters.company) && frappe.get_doc(":Company", filters.company).default_letter_head) || frappe.defaults.get_default("letter_head"); %}
{% if(letterhead) { %}
<div style="margin-bottom: 7px;" class="text-center">
{%= frappe.boot.letter_heads[letterhead].header %}

View File

@ -1,4 +1,4 @@
{% var letterhead= filters.letter_head || frappe.get_doc(":Company", filters.company).default_letter_head || frappe.defaults.get_default("letter_head"); %}
{% var letterhead= filters.letter_head || (frappe.get_doc(":Company", filters.company) && frappe.get_doc(":Company", filters.company).default_letter_head) || frappe.defaults.get_default("letter_head"); %}
{% if(letterhead) { %}
<div style="margin-bottom: 7px;" class="text-center">
{%= frappe.boot.letter_heads[letterhead].header %}

View File

@ -227,7 +227,7 @@ class GrossProfitGenerator(object):
if not average_buying_rate:
average_buying_rate = get_valuation_rate(item_code, row.warehouse,
row.parenttype, row.parent, allow_zero_rate=True,
currency=self.filters.currency)
currency=self.filters.currency, company=self.filters.company)
self.average_buying_rate[item_code] = flt(average_buying_rate)

View File

@ -14,7 +14,7 @@ frappe.query_reports["Item-wise Purchase Register"] = {
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": get_today()
"default": frappe.datetime.get_today()
},
{
"fieldname": "item_code",

View File

@ -14,7 +14,7 @@ frappe.query_reports["Item-wise Sales Register"] = frappe.query_reports["Sales R
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": get_today()
"default": frappe.datetime.get_today()
},
{
"fieldname":"customer",

View File

@ -21,7 +21,7 @@ frappe.query_reports["Payment Period Based On Invoice Date"] = {
fieldname:"to_date",
label: __("To Date"),
fieldtype: "Date",
default: get_today()
default: frappe.datetime.get_today()
},
{
fieldname:"payment_type",

View File

@ -3,6 +3,6 @@
frappe.require("assets/erpnext/js/purchase_trends_filters.js", function() {
frappe.query_reports["Purchase Invoice Trends"] = {
filters: get_filters()
filters: erpnext.get_purchase_trends_filters()
}
});

View File

@ -14,7 +14,7 @@ frappe.query_reports["Purchase Register"] = {
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": get_today()
"default": frappe.datetime.get_today()
},
{
"fieldname":"supplier",

View File

@ -3,6 +3,6 @@
frappe.require("assets/erpnext/js/sales_trends_filters.js", function() {
frappe.query_reports["Sales Invoice Trends"] = {
filters: get_filters()
filters: erpnext.get_sales_trends_filters()
}
});

View File

@ -14,7 +14,7 @@ frappe.query_reports["Sales Register"] = {
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": get_today()
"default": frappe.datetime.get_today()
},
{
"fieldname":"customer",

View File

@ -216,7 +216,8 @@ def get_count_on(account, fieldname, date):
else:
dr_or_cr = "debit" if fieldname == "invoiced_amount" else "credit"
cr_or_dr = "credit" if fieldname == "invoiced_amount" else "debit"
select_fields = "ifnull(sum(credit-debit),0)" if fieldname == "invoiced_amount" else "ifnull(sum(debit-credit),0)"
select_fields = "ifnull(sum(credit-debit),0)" \
if fieldname == "invoiced_amount" else "ifnull(sum(debit-credit),0)"
if ((not gle.against_voucher) or (gle.against_voucher_type in ["Sales Order", "Purchase Order"]) or
(gle.against_voucher==gle.voucher_no and gle.get(dr_or_cr) > 0)):
@ -224,8 +225,10 @@ def get_count_on(account, fieldname, date):
SELECT {0}
FROM `tabGL Entry` gle
WHERE docstatus < 2 and posting_date <= %(date)s and against_voucher = %(voucher_no)s
and party = %(party)s and name != %(name)s""".format(select_fields),
{"date": date, "voucher_no": gle.voucher_no, "party": gle.party, "name": gle.name})[0][0]
and party = %(party)s and name != %(name)s"""
.format(select_fields),
{"date": date, "voucher_no": gle.voucher_no,
"party": gle.party, "name": gle.name})[0][0]
outstanding_amount = flt(gle.get(dr_or_cr)) - flt(gle.get(cr_or_dr)) - payment_amount
currency_precision = get_currency_precision() or 2
@ -519,20 +522,19 @@ def fix_total_debit_credit():
def get_stock_and_account_difference(account_list=None, posting_date=None):
from erpnext.stock.utils import get_stock_value_on
from erpnext.stock import get_warehouse_account_map
if not posting_date: posting_date = nowdate()
difference = {}
warehouse_account = get_warehouse_account_map()
account_warehouse = dict(frappe.db.sql("""select name, warehouse from tabAccount
where account_type = 'Stock' and (warehouse is not null and warehouse != '') and is_group=0
and name in (%s)""" % ', '.join(['%s']*len(account_list)), account_list))
for account, warehouse in account_warehouse.items():
account_balance = get_balance_on(account, posting_date, in_account_currency=False)
stock_value = get_stock_value_on(warehouse, posting_date)
if abs(flt(stock_value) - flt(account_balance)) > 0.005:
difference.setdefault(account, flt(stock_value) - flt(account_balance))
for warehouse, account_data in warehouse_account.items():
if account_data.get('account') in account_list:
account_balance = get_balance_on(account_data.get('account'), posting_date, in_account_currency=False)
stock_value = get_stock_value_on(warehouse, posting_date)
if abs(flt(stock_value) - flt(account_balance)) > 0.005:
difference.setdefault(account, flt(stock_value) - flt(account_balance))
return difference

View File

@ -55,7 +55,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
if(is_drop_ship && doc.status!="Delivered"){
cur_frm.add_custom_button(__('Delivered'),
this.delivered_by_supplier, __("Status"));
this.delivered_by_supplier, __("Status"));
cur_frm.page.set_inner_btn_group_as_primary(__("Status"));
}

View File

@ -43,7 +43,7 @@ class PurchaseOrder(BuyingController):
self.check_for_closed_status()
self.validate_uom_is_integer("uom", "qty")
self.validate_uom_is_integer("stock_uom", ["qty", "required_qty"])
self.validate_uom_is_integer("stock_uom", "stock_qty")
self.validate_with_previous_doc()
self.validate_for_subcontracting()

View File

@ -10,7 +10,7 @@ def get_data():
'internal_links': {
'Material Request': ['items', 'material_request'],
'Supplier Quotation': ['items', 'supplier_quotation'],
'Project': ['project'],
'Project': ['items', 'project'],
},
'transactions': [
{
@ -30,4 +30,4 @@ def get_data():
'items': ['Stock Entry']
}
]
}
}

View File

@ -1,34 +1,34 @@
frappe.listview_settings['Purchase Order'] = {
add_fields: ["base_grand_total", "company", "currency", "supplier",
"supplier_name", "per_received", "per_billed", "status"],
get_indicator: function(doc) {
if(doc.status==="Closed"){
get_indicator: function (doc) {
if (doc.status === "Closed") {
return [__("Closed"), "green", "status,=,Closed"];
} else if (doc.status==="Delivered") {
} else if (doc.status === "Delivered") {
return [__("Delivered"), "green", "status,=,Closed"];
}else if(flt(doc.per_received, 2) < 100 && doc.status!=="Closed") {
if(flt(doc.per_billed, 2) < 100) {
} else if (flt(doc.per_received, 2) < 100 && doc.status !== "Closed") {
if (flt(doc.per_billed, 2) < 100) {
return [__("To Receive and Bill"), "orange",
"per_received,<,100|per_billed,<,100|status,!=,Closed"];
} else {
return [__("To Receive"), "orange",
"per_received,<,100|per_billed,=,100|status,!=,Closed"];
}
} else if(flt(doc.per_received, 2) == 100 && flt(doc.per_billed, 2) < 100 && doc.status!=="Closed") {
} else if (flt(doc.per_received, 2) == 100 && flt(doc.per_billed, 2) < 100 && doc.status !== "Closed") {
return [__("To Bill"), "orange", "per_received,=,100|per_billed,<,100|status,!=,Closed"];
} else if(flt(doc.per_received, 2) == 100 && flt(doc.per_billed, 2) == 100 && doc.status!=="Closed") {
} else if (flt(doc.per_received, 2) == 100 && flt(doc.per_billed, 2) == 100 && doc.status !== "Closed") {
return [__("Completed"), "green", "per_received,=,100|per_billed,=,100|status,!=,Closed"];
}
},
onload: function(listview) {
onload: function (listview) {
var method = "erpnext.buying.doctype.purchase_order.purchase_order.close_or_unclose_purchase_orders";
listview.page.add_menu_item(__("Close"), function() {
listview.call_for_selected_items(method, {"status": "Closed"});
listview.page.add_menu_item(__("Close"), function () {
listview.call_for_selected_items(method, { "status": "Closed" });
});
listview.page.add_menu_item(__("Re-open"), function() {
listview.call_for_selected_items(method, {"status": "Submitted"});
listview.page.add_menu_item(__("Re-open"), function () {
listview.call_for_selected_items(method, { "status": "Submitted" });
});
}
};

View File

@ -56,17 +56,17 @@ frappe.ui.form.on("Request for Quotation",{
var dialog = new frappe.ui.Dialog({
title: __("For Supplier"),
fields: [
{"fieldtype": "Select", "label": __("Supplier"),
"fieldname": "supplier", "options":"Supplier",
"options": $.map(doc.suppliers,
function(d) { return d.supplier }), "reqd": 1 },
{"fieldtype": "Button", "label": __("Make Supplier Quotation"),
"fieldname": "make_supplier_quotation", "cssClass": "btn-primary"},
{ "fieldtype": "Select", "label": __("Supplier"),
"fieldname": "supplier",
"options": doc.suppliers.map(d => d.supplier),
"reqd": 1 },
{ "fieldtype": "Button", "label": __("Make Supplier Quotation"),
"fieldname": "make_supplier_quotation", "cssClass": "btn-primary" },
]
});
dialog.fields_dict.make_supplier_quotation.$input.click(function() {
args = dialog.get_values();
var args = dialog.get_values();
if(!args) return;
dialog.hide();
return frappe.call({
@ -117,7 +117,7 @@ frappe.ui.form.on("Request for Quotation Supplier",{
+"&supplier_idx="+encodeURIComponent(child.idx)
+"&no_letterhead=0"));
if(!w) {
msgprint(__("Please enable pop-ups")); return;
frappe.msgprint(__("Please enable pop-ups")); return;
}
}
})
@ -144,44 +144,44 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
}
})
}, __("Get items from"));
// Get items from open Material Requests based on supplier
this.frm.add_custom_button(__('Possible Supplier'), function() {
// Create a dialog window for the user to pick their supplier
var d = new frappe.ui.Dialog({
title: __('Select Possible Supplier'),
fields: [
{fieldname: 'supplier', fieldtype:'Link', options:'Supplier', label:'Supplier', reqd:1},
{fieldname: 'ok_button', fieldtype:'Button', label:'Get Items from Material Requests'},
]
});
// Get items from open Material Requests based on supplier
this.frm.add_custom_button(__('Possible Supplier'), function() {
// Create a dialog window for the user to pick their supplier
var d = new frappe.ui.Dialog({
title: __('Select Possible Supplier'),
fields: [
{fieldname: 'supplier', fieldtype:'Link', options:'Supplier', label:'Supplier', reqd:1},
{fieldname: 'ok_button', fieldtype:'Button', label:'Get Items from Material Requests'},
]
});
// On the user clicking the ok button
d.fields_dict.ok_button.input.onclick = function() {
var btn = d.fields_dict.ok_button.input;
var v = d.get_values();
if(v) {
$(btn).set_working();
// On the user clicking the ok button
d.fields_dict.ok_button.input.onclick = function() {
var btn = d.fields_dict.ok_button.input;
var v = d.get_values();
if(v) {
$(btn).set_working();
erpnext.utils.map_current_doc({
method: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_item_from_material_requests_based_on_supplier",
source_name: v.supplier,
target: me.frm,
setters: {
company: me.frm.doc.company
},
get_query_filters: {
material_request_type: "Purchase",
docstatus: 1,
status: ["!=", "Stopped"],
per_ordered: ["<", 99.99]
}
});
$(btn).done_working();
d.hide();
}
erpnext.utils.map_current_doc({
method: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_item_from_material_requests_based_on_supplier",
source_name: v.supplier,
target: me.frm,
setters: {
company: me.frm.doc.company
},
get_query_filters: {
material_request_type: "Purchase",
docstatus: 1,
status: ["!=", "Stopped"],
per_ordered: ["<", 99.99]
}
});
$(btn).done_working();
d.hide();
}
d.show();
}, __("Get items from"));
}
d.show();
}, __("Get items from"));
}
},

View File

@ -2,10 +2,10 @@
// License: GNU General Public License v3. See license.txt
frappe.ui.form.on("Supplier", {
setup: function(frm) {
frm.set_query('default_price_list', { 'buying': 1});
frm.set_query('account', 'accounts', function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
setup: function (frm) {
frm.set_query('default_price_list', { 'buying': 1 });
frm.set_query('account', 'accounts', function (doc, cdt, cdn) {
var d = locals[cdt][cdn];
return {
filters: {
'account_type': 'Payable',
@ -15,30 +15,30 @@ frappe.ui.form.on("Supplier", {
}
});
},
refresh: function(frm) {
frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Supplier'}
refresh: function (frm) {
frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Supplier' }
if(frappe.defaults.get_default("supp_master_name")!="Naming Series") {
if (frappe.defaults.get_default("supp_master_name") != "Naming Series") {
frm.toggle_display("naming_series", false);
} else {
erpnext.toggle_naming_series();
}
if(frm.doc.__islocal){
hide_field(['address_html','contact_html']);
frappe.geo.clear_address_and_contact(frm);
if (frm.doc.__islocal) {
hide_field(['address_html','contact_html']);
frappe.contacts.clear_address_and_contact(frm);
}
else {
unhide_field(['address_html','contact_html']);
frappe.geo.render_address_and_contact(frm);
unhide_field(['address_html','contact_html']);
frappe.contacts.render_address_and_contact(frm);
// custom buttons
frm.add_custom_button(__('Accounting Ledger'), function() {
frm.add_custom_button(__('Accounting Ledger'), function () {
frappe.set_route('query-report', 'General Ledger',
{party_type:'Supplier', party:frm.doc.name});
{ party_type: 'Supplier', party: frm.doc.name });
});
frm.add_custom_button(__('Accounts Payable'), function() {
frappe.set_route('query-report', 'Accounts Payable', {supplier:frm.doc.name});
frm.add_custom_button(__('Accounts Payable'), function () {
frappe.set_route('query-report', 'Accounts Payable', { supplier: frm.doc.name });
});
// indicators

View File

@ -6,7 +6,7 @@ import frappe
import frappe.defaults
from frappe import msgprint, _
from frappe.model.naming import make_autoname
from frappe.geo.address_and_contact import load_address_and_contact, delete_contact_and_address
from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
from erpnext.utilities.transaction_base import TransactionBase
from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this

View File

@ -5,7 +5,7 @@
{% include 'erpnext/public/js/controllers/buying.js' %};
frappe.ui.form.on('Suppier Quotation', {
setup: function() {
setup: function(frm) {
frm.custom_make_buttons = {
'Purchase Order': 'Purchase Order'
}

View File

@ -10,7 +10,6 @@ frappe.pages['purchase-analytics'].on_page_load = function(wrapper) {
new erpnext.PurchaseAnalytics(wrapper);
frappe.breadcrumbs.add("Buying");
}
@ -18,7 +17,6 @@ erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
init: function(wrapper) {
this._super({
title: __("Purchase Analytics"),
page: wrapper,
parent: $(wrapper).find('.layout-main'),
page: wrapper.page,
doctypes: ["Item", "Item Group", "Supplier", "Supplier Type", "Company", "Fiscal Year",
@ -193,13 +191,13 @@ erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
},
prepare_balances: function() {
var me = this;
var from_date = dateutil.str_to_obj(this.from_date);
var to_date = dateutil.str_to_obj(this.to_date);
var from_date = frappe.datetime.str_to_obj(this.from_date);
var to_date = frappe.datetime.str_to_obj(this.to_date);
var is_val = this.value_or_qty == 'Value';
$.each(this.tl[this.based_on], function(i, tl) {
if (me.is_default('company') ? true : tl.company === me.company) {
var posting_date = dateutil.str_to_obj(tl.posting_date);
var posting_date = frappe.datetime.str_to_obj(tl.posting_date);
if (posting_date >= from_date && posting_date <= to_date) {
var item = me.item_by_name[tl[me.tree_grid.item_key]] ||
me.item_by_name['Not Set'];

View File

@ -3,6 +3,6 @@
frappe.require("assets/erpnext/js/purchase_trends_filters.js", function() {
frappe.query_reports["Purchase Order Trends"] = {
filters: get_filters()
filters: erpnext.get_purchase_trends_filters()
}
});

View File

@ -3,30 +3,26 @@
frappe.query_reports["Quoted Item Comparison"] = {
"filters": [
{
"fieldname":"supplier_quotation",
"label": __("Supplier Quotation"),
"fieldtype": "Link",
"options": "Supplier Quotation",
"default": "",
"get_query": function() {
return {
filters: {"docstatus": ["<",2]}
}
{
"fieldname": "supplier_quotation",
"label": __("Supplier Quotation"),
"fieldtype": "Link",
"options": "Supplier Quotation",
"default": "",
"get_query": function () {
return { filters: { "docstatus": ["<", 2] } }
}
},{
"fieldname":"item",
"label": __("Item"),
"fieldtype": "Link",
"options": "Item",
"default": "",
"reqd": 1,
"get_query": function() {
},
{
"fieldname": "item",
"label": __("Item"),
"fieldtype": "Link",
"options": "Item",
"default": "",
"reqd": 1,
"get_query": function () {
var quote = frappe.query_report_filters_by_name.supplier_quotation.get_value();
if (quote != "")
{
if (quote != "") {
return {
query: "erpnext.buying.doctype.quality_inspection.quality_inspection.item_query",
filters: {
@ -35,79 +31,74 @@ frappe.query_reports["Quoted Item Comparison"] = {
}
}
}
else{
return{
filters: {"disabled":0}
else {
return {
filters: { "disabled": 0 }
}
}
}
}
}
],
onload: function(report) {
//Create a button for setting the default supplier
report.page.add_inner_button(__("Select Default Supplier"), function() {
onload: function (report) {
// Create a button for setting the default supplier
report.page.add_inner_button(__("Select Default Supplier"), function () {
var reporter = frappe.query_reports["Quoted Item Comparison"];
//Always make a new one so that the latest values get updated
reporter.make_default_supplier_dialog(report);
report.dialog.show();
setTimeout(function() { report.dialog.input.focus(); }, 1000);
setTimeout(function () { report.dialog.input.focus(); }, 1000);
}, 'Tools');
},
"make_default_supplier_dialog": function (report) {
//Get the name of the item to change
// Get the name of the item to change
var filters = report.get_values();
var item_code = filters.item;
//Get a list of the suppliers (with a blank as well) for the user to select
// Get a list of the suppliers (with a blank as well) for the user to select
var select_options = "";
for (let supplier of report.data)
{
select_options += supplier.supplier_name+ '\n'
for (let supplier of report.data) {
select_options += supplier.supplier_name + '\n'
}
//Create a dialog window for the user to pick their supplier
// Create a dialog window for the user to pick their supplier
var d = new frappe.ui.Dialog({
title: __('Select Default Supplier'),
fields: [
{fieldname: 'supplier', fieldtype:'Select', label:'Supplier', reqd:1,options:select_options},
{fieldname: 'ok_button', fieldtype:'Button', label:'Set Default Supplier'},
{ fieldname: 'supplier', fieldtype: 'Select', label: 'Supplier', reqd: 1, options: select_options },
{ fieldname: 'ok_button', fieldtype: 'Button', label: 'Set Default Supplier' },
]
});
//On the user clicking the ok button
d.fields_dict.ok_button.input.onclick = function() {
// On the user clicking the ok button
d.fields_dict.ok_button.input.onclick = function () {
var btn = d.fields_dict.ok_button.input;
var v = report.dialog.get_values();
if(v) {
if (v) {
$(btn).set_working();
//Set the default_supplier field of the appropriate Item to the selected supplier
// Set the default_supplier field of the appropriate Item to the selected supplier
frappe.call({
method: "frappe.client.set_value",
args: {
doctype: "Item",
doctype: "Item",
name: item_code,
fieldname: "default_supplier",
value: v.supplier,
value: v.supplier,
},
callback: function (r){
callback: function (r) {
$(btn).done_working();
msgprint("Successfully Set Supplier");
frappe.msgprint("Successfully Set Supplier");
report.dialog.hide();
}
});
}
}
report.dialog = d;
}
}

View File

@ -0,0 +1,49 @@
# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals, absolute_import, print_function
import click
import frappe
from frappe.commands import pass_context, get_site
def call_command(cmd, context):
return click.Context(cmd, obj=context).forward(cmd)
@click.command('make-demo')
@click.option('--site', help='site name')
@click.option('--domain', default='Manufacturing')
@click.option('--days', default=100,
help='Run the demo for so many days. Default 100')
@click.option('--resume', default=False, is_flag=True,
help='Continue running the demo for given days')
@click.option('--reinstall', default=False, is_flag=True,
help='Reinstall site before demo')
@pass_context
def make_demo(context, site, domain='Manufacturing', days=100,
resume=False, reinstall=False):
"Reinstall site and setup demo"
from frappe.commands.site import _reinstall
from frappe.installer import install_app
site = get_site(context)
if resume:
with frappe.init_site(site):
frappe.connect()
from erpnext.demo import demo
demo.simulate(days=days)
else:
if reinstall:
_reinstall(site, yes=True)
with frappe.init_site(site=site):
frappe.connect()
if not 'erpnext' in frappe.get_installed_apps():
install_app('erpnext')
# import needs site
from erpnext.demo import demo
demo.make(domain, days)
commands = [
make_demo
]

View File

@ -121,7 +121,7 @@ def get_data():
"type": "report",
"is_query_report": True,
"name": "Addresses And Contacts",
"label": "Sales Partner Addresses And Contacts",
"label": _("Sales Partner Addresses And Contacts"),
"doctype": "Address",
"route_options": {
"party_type": "Sales Partner"
@ -226,7 +226,7 @@ def get_data():
"type": "report",
"is_query_report": True,
"name": "Addresses And Contacts",
"label": "Customer Addresses And Contacts",
"label": _("Customer Addresses And Contacts"),
"doctype": "Address",
"route_options": {
"party_type": "Customer"

View File

@ -274,7 +274,9 @@ class AccountsController(TransactionBase):
if not account_currency:
account_currency = get_account_currency(gl_dict.account)
if self.doctype not in ["Journal Entry", "Period Closing Voucher", "Payment Entry"]:
if gl_dict.account and self.doctype not in ["Journal Entry",
"Period Closing Voucher", "Payment Entry"]:
self.validate_account_currency(gl_dict.account, account_currency)
set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"), self.company_currency)

View File

@ -73,10 +73,13 @@ class BuyingController(StockController):
def validate_stock_or_nonstock_items(self):
if self.meta.get_field("taxes") and not self.get_stock_items():
tax_for_valuation = [d.account_head for d in self.get("taxes")
tax_for_valuation = [d for d in self.get("taxes")
if d.category in ["Valuation", "Valuation and Total"]]
if tax_for_valuation:
frappe.throw(_("Tax Category can not be 'Valuation' or 'Valuation and Total' as all items are non-stock items"))
for d in tax_for_valuation:
d.category = 'Total'
msgprint(_('Tax Category has been changed to "Total" because all the Items are non-stock items'))
def set_landed_cost_voucher_amount(self):
for d in self.get("items"):
@ -224,7 +227,7 @@ class BuyingController(StockController):
})
if not rm.rate:
rm.rate = get_valuation_rate(bom_item.item_code, self.supplier_warehouse,
self.doctype, self.name, currency=self.company_currency)
self.doctype, self.name, currency=self.company_currency, company = self.company)
else:
rm.rate = bom_item.rate

View File

@ -71,7 +71,7 @@ def validate_returned_items(doc):
already_returned_items = get_already_returned_items(doc)
# ( not mandatory when it is Purchase Invoice or a Sales Invoice without Update Stock )
warehouse_mandatory = not (doc.doctype=="Purchase Invoice" or (doc.doctype=="Sales Invoice" and not doc.update_stock))
warehouse_mandatory = not ((doc.doctype=="Purchase Invoice" or doc.doctype=="Sales Invoice") and not doc.update_stock)
items_returned = False
for d in doc.get("items"):
@ -101,8 +101,9 @@ def validate_returned_items(doc):
frappe.throw(_("Row # {0}: Serial No {1} does not match with {2} {3}")
.format(d.idx, s, doc.doctype, doc.return_against))
if warehouse_mandatory and not d.get("warehouse"):
frappe.throw(_("Warehouse is mandatory"))
if warehouse_mandatory and frappe.db.get_value("Item", d.item_code, "is_stock_item") \
and not d.get("warehouse"):
frappe.throw(_("Warehouse is mandatory"))
items_returned = True

View File

@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
import frappe, erpnext
from frappe.utils import cint, flt, cstr
from frappe import msgprint, _
import frappe.defaults
@ -10,6 +10,7 @@ from erpnext.accounts.utils import get_fiscal_year
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.stock.stock_ledger import get_valuation_rate
from erpnext.stock import get_warehouse_account_map
class StockController(AccountsController):
def validate(self):
@ -20,8 +21,8 @@ class StockController(AccountsController):
if self.docstatus == 2:
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
warehouse_account = get_warehouse_account()
if cint(erpnext.is_perpetual_inventory_enabled(self.company)):
warehouse_account = get_warehouse_account_map()
if self.docstatus==1:
if not gl_entries:
@ -37,7 +38,7 @@ class StockController(AccountsController):
default_cost_center=None):
if not warehouse_account:
warehouse_account = get_warehouse_account()
warehouse_account = get_warehouse_account_map()
sle_map = self.get_stock_ledger_details()
voucher_details = self.get_voucher_details(default_expense_account, default_cost_center, sle_map)
@ -66,7 +67,7 @@ class StockController(AccountsController):
sle = self.update_stock_ledger_entries(sle)
gl_list.append(self.get_gl_dict({
"account": warehouse_account[sle.warehouse]["name"],
"account": warehouse_account[sle.warehouse]["account"],
"against": item_row.expense_account,
"cost_center": item_row.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock",
@ -76,7 +77,7 @@ class StockController(AccountsController):
# to target warehouse / expense account
gl_list.append(self.get_gl_dict({
"account": item_row.expense_account,
"against": warehouse_account[sle.warehouse]["name"],
"against": warehouse_account[sle.warehouse]["account"],
"cost_center": item_row.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock",
"credit": flt(sle.stock_value_difference, 2),
@ -88,16 +89,13 @@ class StockController(AccountsController):
if warehouse_with_no_account:
for wh in warehouse_with_no_account:
if frappe.db.get_value("Warehouse", wh, "company"):
frappe.throw(_("Warehouse {0} is not linked to any account, please create/link the corresponding (Asset) account for the warehouse.").format(wh))
msgprint(_("No accounting entries for the following warehouses") + ": \n" +
"\n".join(warehouse_with_no_account))
frappe.throw(_("Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}.").format(wh, self.company))
return process_gl_map(gl_list)
def update_stock_ledger_entries(self, sle):
sle.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
self.doctype, self.name, currency=self.company_currency)
self.doctype, self.name, currency=self.company_currency, company=self.company)
sle.stock_value = flt(sle.qty_after_transaction) * flt(sle.valuation_rate)
sle.stock_value_difference = flt(sle.actual_qty) * flt(sle.valuation_rate)
@ -341,7 +339,7 @@ def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
if not warehouse_account:
warehouse_account = get_warehouse_account()
warehouse_account = get_warehouse_account_map()
future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
@ -405,22 +403,4 @@ def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
tuple([posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1):
gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
return gl_entries
def get_warehouse_account():
if not frappe.flags.warehouse_account_map or frappe.flags.in_test:
warehouse_account = frappe._dict()
for d in frappe.db.sql("""select
warehouse, name, account_currency
from
tabAccount
where
account_type = 'Stock'
and (warehouse is not null and warehouse != '')
and is_group=0 """, as_dict=1):
warehouse_account.setdefault(d.warehouse, d)
frappe.flags.warehouse_account_map = warehouse_account
return frappe.flags.warehouse_account_map
return gl_entries

View File

@ -7,27 +7,28 @@ cur_frm.email_field = "email_id";
erpnext.LeadController = frappe.ui.form.Controller.extend({
setup: function() {
this.frm.fields_dict.customer.get_query = function(doc, cdt, cdn) {
return { query: "erpnext.controllers.queries.customer_query" } }
return { query: "erpnext.controllers.queries.customer_query" } }
},
onload: function() {
if(cur_frm.fields_dict.lead_owner.df.options.match(/^User/)) {
cur_frm.fields_dict.lead_owner.get_query = function(doc, cdt, cdn) {
return { query:"frappe.core.doctype.user.user.user_query" } }
return { query: "frappe.core.doctype.user.user.user_query" }
}
}
if(cur_frm.fields_dict.contact_by.df.options.match(/^User/)) {
cur_frm.fields_dict.contact_by.get_query = function(doc, cdt, cdn) {
return { query:"frappe.core.doctype.user.user.user_query" } }
return { query: "frappe.core.doctype.user.user.user_query" } }
}
},
refresh: function() {
var doc = this.frm.doc;
erpnext.toggle_naming_series();
frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'name', doctype: 'Lead'}
frappe.dynamic_link = {doc: doc, fieldname: 'name', doctype: 'Lead'}
if(!this.frm.doc.__islocal && this.frm.doc.__onload && !this.frm.doc.__onload.is_customer) {
if(!doc.__islocal && doc.__onload && !doc.__onload.is_customer) {
this.frm.add_custom_button(__("Customer"), this.create_customer, __("Make"));
this.frm.add_custom_button(__("Opportunity"), this.create_opportunity, __("Make"));
this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Make"));
@ -35,9 +36,9 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
}
if(!this.frm.doc.__islocal) {
frappe.geo.render_address_and_contact(cur_frm);
frappe.contacts.render_address_and_contact(cur_frm);
} else {
frappe.geo.clear_address_and_contact(cur_frm);
frappe.contacts.clear_address_and_contact(cur_frm);
}
},

View File

@ -76,6 +76,36 @@
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "salutation",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Salutation",
"length": 0,
"no_copy": 0,
"options": "Salutation",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -111,21 +141,21 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company_name",
"fieldtype": "Data",
"fieldname": "gender",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Organization Name",
"label": "Gender",
"length": 0,
"no_copy": 0,
"oldfieldname": "company_name",
"oldfieldtype": "Data",
"options": "Gender",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@ -265,6 +295,36 @@
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Organization Name",
"length": 0,
"no_copy": 0,
"oldfieldname": "company_name",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -1088,6 +1148,7 @@
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead",
"name_case": "Title Case",
"owner": "Administrator",
"permissions": [
{

View File

@ -4,11 +4,11 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import (cstr, validate_email_add, cint, comma_and, has_gravatar, now)
from frappe.utils import (cstr, validate_email_add, cint, comma_and, has_gravatar, now, getdate, nowdate)
from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.selling_controller import SellingController
from frappe.geo.address_and_contact import load_address_and_contact
from frappe.contacts.address_and_contact import load_address_and_contact
from erpnext.accounts.party import set_taxes
sender_field = "email_id"
@ -46,7 +46,7 @@ class Lead(SellingController):
if self.is_new() or not self.image:
self.image = has_gravatar(self.email_id)
if self.contact_date and self.contact_date < now():
if self.contact_date and getdate(self.contact_date) < getdate(nowdate()):
frappe.throw(_("Next Contact Date cannot be in the past"))
def on_update(self):

View File

@ -122,7 +122,8 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
$.each([["lead", "lead"],
["customer", "customer"],
["contact_person", "contact_query"]], function(i, opts) {
["contact_person", "contact_query"]],
function(i, opts) {
me.frm.set_query(opts[0], erpnext.queries[opts[1]]);
});
},
@ -153,7 +154,7 @@ cur_frm.cscript.item_code = function(doc, cdt, cdn) {
$.each(r.message, function(k, v) {
frappe.model.set_value(cdt, cdn, k, v);
});
refresh_field('image_view', d.name, 'items');
refresh_field('image_view', d.name, 'items');
}
}
})
@ -180,7 +181,7 @@ cur_frm.cscript['Declare Opportunity Lost'] = function() {
});
dialog.fields_dict.update.$input.click(function() {
args = dialog.get_values();
var args = dialog.get_values();
if(!args) return;
return cur_frm.call({
doc: cur_frm.doc,
@ -188,7 +189,7 @@ cur_frm.cscript['Declare Opportunity Lost'] = function() {
args: args.reason,
callback: function(r) {
if(r.exc) {
msgprint(__("There were errors."));
frappe.msgprint(__("There were errors."));
} else {
dialog.hide();
cur_frm.refresh();

View File

@ -1,19 +1,19 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.query_reports["Campaign Efficiency"] = {
"filters": [
{
"fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_start_date"),
},
{
"fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_end_date"),
}
]
};
"filters": [
{
"fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_start_date"),
},
{
"fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_end_date"),
}
]
};

View File

@ -2,40 +2,40 @@
// For license information, please see license.txt
frappe.query_reports["Minutes to First Response for Opportunity"] = {
"filters": [
{
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
"filters": [
{
"fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
'reqd': 1,
"default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30)
},
{
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30)
},
{
"fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
'reqd': 1,
"default":frappe.datetime.nowdate()
},
],
get_chart_data: function(columns, result) {
"default": frappe.datetime.nowdate()
},
],
get_chart_data: function (columns, result) {
return {
data: {
x: 'Date',
columns: [
['Date'].concat($.map(result, function(d) { return d[0]; })),
['Mins to first response'].concat($.map(result, function(d) { return d[1]; }))
['Date'].concat($.map(result, function (d) { return d[0]; })),
['Mins to first response'].concat($.map(result, function (d) { return d[1]; }))
]
// rows: [['Date', 'Mins to first response']].concat(result)
},
axis: {
x: {
type: 'timeseries',
tick: {
format: frappe.ui.py_date_format
}
}
},
axis: {
x: {
type: 'timeseries',
tick: {
format: frappe.ui.py_date_format
}
}
},
chart_type: 'line',
}

View File

@ -0,0 +1,18 @@
[
{
"doctype": "Assessment Criteria",
"assessment_criteria": "Aptitude"
},
{
"doctype": "Assessment Criteria",
"assessment_criteria": "Application"
},
{
"doctype": "Assessment Criteria",
"assessment_criteria": "Understanding"
},
{
"doctype": "Assessment Criteria",
"assessment_criteria": "Knowledge"
}
]

View File

@ -0,0 +1,17 @@
[
{
"doctype": "Grading Scale",
"grading_scale_name": "Standard Grading",
"description": "Standard Grading Scale",
"intervals": [
{"threshold": 100.0, "grade_code": "A", "grade_description": "Excellent"},
{"threshold": 89.9, "grade_code": "B+", "grade_description": "Close to Excellence"},
{"threshold": 80.0, "grade_code": "B", "grade_description": "Good"},
{"threshold": 69.9, "grade_code": "C+", "grade_description": "Almost Good"},
{"threshold": 60.0, "grade_code": "C", "grade_description": "Average"},
{"threshold": 50.0, "grade_code": "D+", "grade_description": "Have to Work"},
{"threshold": 40.0, "grade_code": "D", "grade_description": "Not met Baseline Expectations"},
{"threshold": 0.0, "grade_code": "F", "grade_description": "Have to work a lot"}
]
}
]

View File

@ -57,6 +57,7 @@
},
{
"is_stock_item": 0,
"default_warehouse": "Stores",
"description": "Computer",
"item_code": "Computer",
"item_name": "Computer",
@ -64,6 +65,7 @@
},
{
"is_stock_item": 0,
"default_warehouse": "Stores",
"description": "Mobile",
"item_code": "Mobile",
"item_name": "Mobile",
@ -71,6 +73,7 @@
},
{
"is_stock_item": 0,
"default_warehouse": "Stores",
"description": "ERP",
"item_code": "ERP",
"item_name": "ERP",
@ -78,12 +81,14 @@
},
{
"is_stock_item": 0,
"default_warehouse": "Stores",
"description": "Autocad",
"item_code": "Autocad",
"item_name": "Autocad",
"item_group": "All Item Groups"
},
{
"default_warehouse": "Stores",
"default_warehouse": "Stores",
"item_code": "Service",
"item_group": "Services",

View File

@ -1,102 +1,46 @@
[
{
"doctype": "Program",
"name": "MCA",
"program_name": "Masters of Computer Applications",
"program_code": "MCA",
"department": "Information Technology",
"courses": [
{
"course": "Microprocessor",
"academic_term": "Semester 1"
},
{
"course": "Probability and Statistics",
"academic_term": "Semester 1"
},
{
"course": "Programing in Java",
"academic_term": "Semester 2"
}
{ "course": "MCA4010" },
{ "course": "MCA4020" },
{ "course": "MCA4030" }
]
},
{
"doctype": "Program",
"name": "BCA",
"program_name": "Bachelor of Computer Applications",
"program_code": "BCA",
"department": "Information Technology",
"courses": [
{
"course": "Communication Skiils",
"academic_term": "Semester 3"
},
{
"course": "Object Oriented Programing - C++",
"academic_term": "Semester 3"
},
{
"course": "Basic Mathematics",
"academic_term": "Semester 2"
},
{
"course": "Data Structures and Algorithm",
"academic_term": "Semester 2"
},
{
"course": "Digital Logic",
"academic_term": "Semester 2"
},
{
"course": "Fundamentals of IT & Programing",
"academic_term": "Semester 1"
},
{
"course": "Operating System",
"academic_term": "Semester 1"
},
{
"course": "Programing in C",
"academic_term": "Semester 1"
}
{ "course": "BCA2030" },
{ "course": "BCA1030" },
{ "course": "BCA2020" },
{ "course": "BCA1040" },
{ "course": "BCA1010" },
{ "course": "BCA2010" },
{ "course": "BCA1020" }
]
},
{
"doctype": "Program",
"name": "BBA",
"program_name": "Bachelor of Business Administration",
"program_code": "BBA",
"department": "Management Studies",
"courses": [
{
"course": "Organizational Behavior",
"academic_term": "Semester 1"
},
{
"course": "Management Development and Skills",
"academic_term": "Semester 1"
},
{
"course": "Legal and Regulatory Framework",
"academic_term": "Semester 1"
},
{
"course": "Human Resource Management",
"academic_term": "Semester 2"
},
{
"course": "Entrepreneurship Management",
"academic_term": "Semester 2"
},
{
"course": "Communication Skills",
"academic_term": "Semester 2"
},
{
"course": "Business Environment",
"academic_term": "Semester 3"
},
{
"course": "Advertising and Sales",
"academic_term": "Semester 3"
}
{ "course": "BBA 101" },
{ "course": "BBA 102" },
{ "course": "BBA 103" },
{ "course": "BBA 301" },
{ "course": "BBA 302" },
{ "course": "BBA 304" },
{ "course": "BBA 505" }
]
}
]

View File

@ -0,0 +1,10 @@
[
{
"doctype": "Student Batch Name",
"batch_name": "Section-A"
},
{
"doctype": "Student Batch Name",
"batch_name": "Section-B"
}
]

View File

@ -22,7 +22,7 @@ bench --site demo.erpnext.dev execute erpnext.demo.demo.simulate
"""
def make(domain='Manufacturing'):
def make(domain='Manufacturing', days=100):
frappe.flags.domain = domain
frappe.flags.mute_emails = True
setup_data.setup(domain)
@ -35,16 +35,18 @@ def make(domain='Manufacturing'):
frappe.destroy()
frappe.init(site)
frappe.connect()
simulate(domain)
def simulate(domain='Manufacturing'):
runs_for = frappe.flags.runs_for or 150
simulate(domain, days)
def simulate(domain='Manufacturing', days=100):
runs_for = frappe.flags.runs_for or days
frappe.flags.company = erpnext.get_default_company()
frappe.flags.mute_emails = True
if not frappe.flags.start_date:
# start date = 100 days back
frappe.flags.start_date = frappe.utils.add_days(frappe.utils.nowdate(), -1 * runs_for)
frappe.flags.start_date = frappe.utils.add_days(frappe.utils.nowdate(),
-1 * runs_for)
current_date = frappe.utils.getdate(frappe.flags.start_date)
@ -73,7 +75,7 @@ def simulate(domain='Manufacturing'):
stock.work()
accounts.work()
projects.run_projects(current_date)
#run_messages()
# run_messages()
if domain=='Manufacturing':
sales.work()

View File

@ -15,6 +15,7 @@ def setup_data():
make_student_group()
make_fees_category()
make_fees_structure()
make_assessment_groups()
frappe.db.commit()
frappe.clear_cache()
@ -24,6 +25,9 @@ def make_masters():
import_json("Instructor")
import_json("Course")
import_json("Program")
import_json("Student Batch Name")
import_json("Assessment Criteria")
import_json("Grading Scale")
frappe.db.commit()
def setup_item():
@ -81,13 +85,24 @@ def make_student_applicants():
count+=1
def make_student_group():
for d in frappe.db.get_list("Academic Term"):
sg_tool = frappe.new_doc("Student Group Creation Tool")
sg_tool.academic_year = "2016-17"
sg_tool.academic_term = d.name
sg_tool.courses = sg_tool.get_courses()
sg_tool.create_student_groups()
frappe.db.commit()
for term in frappe.db.get_list("Academic Term"):
for program in frappe.db.get_list("Program"):
sg_tool = frappe.new_doc("Student Group Creation Tool")
sg_tool.academic_year = "2017-18"
sg_tool.academic_term = term.name
sg_tool.program = program.name
for d in sg_tool.get_courses():
d = frappe._dict(d)
student_group = frappe.new_doc("Student Group")
student_group.student_group_name = d.student_group_name
student_group.group_based_on = d.group_based_on
student_group.program = program.name
student_group.course = d.course
student_group.batch = d.batch
student_group.academic_term = term.name
student_group.academic_year = "2017-18"
student_group.save()
frappe.db.commit()
def make_fees_category():
fee_type = ["Tuition Fee", "Hostel Fee", "Logistics Fee",
@ -111,7 +126,7 @@ def make_fees_category():
def make_fees_structure():
for d in frappe.db.get_list("Program"):
program = frappe.get_doc("Program", d.name)
for academic_term in ["Semester 1", "Semester 2", "Semester 3"]:
for academic_term in ["2017-18 (Semester 1)", "2017-18 (Semester 2)", "2017-18 (Semester 3)"]:
fee_structure = frappe.new_doc("Fee Structure")
fee_structure.program = d.name
fee_structure.academic_term = random.choice(frappe.db.get_list("Academic Term")).name
@ -123,6 +138,27 @@ def make_fees_structure():
program.save()
frappe.db.commit()
def make_assessment_groups():
for year in frappe.db.get_list("Academic Year"):
ag = frappe.new_doc('Assessment Group')
ag.assessment_group_name = year.name
ag.parent_assessment_group = "All Assessment Groups"
ag.is_group = 1
ag.insert()
for term in frappe.db.get_list("Academic Term", filters = {"academic_year": year.name}):
ag1 = frappe.new_doc('Assessment Group')
ag1.assessment_group_name = term.name
ag1.parent_assessment_group = ag.name
ag1.is_group = 1
ag1.insert()
for assessment_group in ['Term I', 'Term II']:
ag2 = frappe.new_doc('Assessment Group')
ag2.assessment_group_name = ag1.name + " " + assessment_group
ag2.parent_assessment_group = ag1.name
ag2.insert()
frappe.db.commit()
def get_json_path(doctype):
return frappe.get_app_path('erpnext', 'demo', 'data', frappe.scrub(doctype) + '.json')

View File

@ -116,7 +116,7 @@ def setup_user():
for u in json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data', 'user.json')).read()):
user = frappe.new_doc("User")
user.update(u)
user.flags.no_welcome_mail
user.flags.no_welcome_mail = True
user.new_password = 'demo'
user.insert()

Some files were not shown because too many files have changed in this diff Show More