Merge branch 'develop'
This commit is contained in:
commit
78aac9edb6
136
.eslintrc
Normal file
136
.eslintrc
Normal 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
|
||||
}
|
||||
}
|
@ -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]
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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"""
|
||||
|
@ -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");
|
||||
|
@ -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")
|
||||
|
@ -88,7 +88,6 @@
|
||||
"Items Delivered to Customs on temprary Base": {}
|
||||
},
|
||||
"Stock in Hand": {
|
||||
"is_group": 1,
|
||||
"account_type": "Stock"
|
||||
}
|
||||
},
|
||||
|
@ -65,8 +65,9 @@
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
"Stock": {
|
||||
"account_type": "Stock",
|
||||
"is_group": 1
|
||||
"Stock in Hand": {
|
||||
"account_type": "Stock"
|
||||
}
|
||||
},
|
||||
"root_type": "Asset"
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -40,8 +40,9 @@
|
||||
"Rental Deposits": {}
|
||||
},
|
||||
"Stock Assets": {
|
||||
"is_group": 1,
|
||||
"account_type": "Stock"
|
||||
"Stock in Hand": {
|
||||
"account_type": "Stock"
|
||||
}
|
||||
},
|
||||
"Tax Assets": {
|
||||
"GST-Input": {}
|
||||
|
@ -40,8 +40,9 @@
|
||||
"Rental Deposits": {}
|
||||
},
|
||||
"Stock Assets": {
|
||||
"account_type": "Stock",
|
||||
"is_group": 1
|
||||
"Stock in Hand": {
|
||||
"account_type": "Stock"
|
||||
}
|
||||
},
|
||||
"Tax Assets": {
|
||||
"GST-Input": {}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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
|
@ -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({
|
||||
|
@ -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)
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
frappe.listview_settings['Asset'] = {
|
||||
add_fields: ['image']
|
||||
add_fields: ['image']
|
||||
}
|
@ -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: [
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
@ -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'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
},
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
})
|
||||
|
@ -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))
|
||||
|
@ -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){
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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):
|
||||
|
@ -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]
|
||||
])
|
||||
|
||||
|
@ -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');
|
||||
|
@ -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){
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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++
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
},
|
||||
{
|
||||
|
@ -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"
|
||||
},
|
||||
{
|
||||
|
@ -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 %}
|
||||
|
@ -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"
|
||||
},
|
||||
{
|
||||
|
@ -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 });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
},
|
||||
]
|
||||
|
@ -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
|
||||
},
|
||||
{
|
||||
|
@ -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 %}
|
||||
|
@ -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 %}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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()
|
||||
}
|
||||
});
|
@ -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",
|
||||
|
@ -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()
|
||||
}
|
||||
});
|
@ -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",
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"));
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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']
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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" });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -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"));
|
||||
|
||||
}
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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'
|
||||
}
|
||||
|
@ -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'];
|
||||
|
@ -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()
|
||||
}
|
||||
});
|
@ -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;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
49
erpnext/commands/__init__.py
Normal file
49
erpnext/commands/__init__.py
Normal 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
|
||||
]
|
@ -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"
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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": [
|
||||
{
|
||||
|
@ -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):
|
||||
|
@ -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();
|
||||
|
@ -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"),
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
|
@ -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',
|
||||
|
||||
}
|
||||
|
18
erpnext/demo/data/assessment_criteria.json
Normal file
18
erpnext/demo/data/assessment_criteria.json
Normal 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"
|
||||
}
|
||||
]
|
17
erpnext/demo/data/grading_scale.json
Normal file
17
erpnext/demo/data/grading_scale.json
Normal 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"}
|
||||
]
|
||||
}
|
||||
]
|
@ -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",
|
||||
|
@ -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" }
|
||||
]
|
||||
}
|
||||
]
|
10
erpnext/demo/data/student_batch_name.json
Normal file
10
erpnext/demo/data/student_batch_name.json
Normal file
@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"doctype": "Student Batch Name",
|
||||
"batch_name": "Section-A"
|
||||
},
|
||||
{
|
||||
"doctype": "Student Batch Name",
|
||||
"batch_name": "Section-B"
|
||||
}
|
||||
]
|
@ -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()
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user