Compare commits

...

49 Commits

Author SHA1 Message Date
Nabin Hait
5e9d72d57b Merge branch 'develop' 2017-04-06 16:32:04 +05:30
Nabin Hait
0d230afd22 bumped to version 8.0.8 2017-04-06 17:02:03 +06:00
Makis Etzoglou
a906b3f11c add delivery date to print format Fix #8111 2017-04-06 15:47:52 +05:30
Kanchan Chauhan
aa6f00c403 Added default 'All' option to filters to differentiate all and empty filter 2017-04-06 15:22:18 +05:30
Nabin Hait
d681c88d79 validate returned qty only if there is reference qty 2017-04-06 15:20:45 +05:30
Javier Wong
fc11fce990 [enhancement] Changed Issue Description and Resolution to Text Editor (#8346) 2017-04-06 14:56:44 +05:30
Rohit Waghchaure
faf51d91b6 [fix] stock qty issue in purchase trends report 2017-04-05 14:53:26 +05:30
Rushabh Mehta
08a42e00ae [fix] salary slip eval, remove access to globals, #8313 2017-04-05 14:52:22 +05:30
Nabin Hait
549a2827e5 Merge pull request #8300 from rohitwaghchaure/customer_edit_issue_v8
[fix] Customer edit issue in offline POS
2017-04-05 14:51:44 +05:30
Rushabh Mehta
9264313f0e [fix] ordering for Edit Posting Time check 2017-04-05 12:49:01 +05:30
Nabin Hait
8013d1813a Merge pull request #8320 from manassolanki/newsl
Route to newsletter listview
2017-04-05 12:33:14 +05:30
Rushabh Mehta
ffc807cd57 [minor] check below posting date in delivery note 2017-04-05 12:27:41 +05:30
Manas Solanki
ba6559c2e9 Route to newsletter listview 2017-04-05 11:52:23 +05:30
Nabin Hait
7eea52d4e9 Merge branch 'develop' 2017-04-04 11:46:47 +05:30
Nabin Hait
93a990f006 bumped to version 8.0.7 2017-04-04 12:16:47 +06:00
Rohit Waghchaure
2861955632 [fix] Customer edit issue in offline POS 2017-04-04 09:59:29 +05:30
Nabin Hait
099163b16d Merge pull request #8296 from netchampfaris/customer-list-fix
Add `image` field to fetch in Customer List
2017-04-04 09:39:22 +05:30
Faris Ansari
0d114b4c31 Add image field to fetch in Customer List 2017-04-03 21:21:50 +05:30
Nabin Hait
fcf34dc570 Merge pull request #8295 from saurabh6790/demo_fix
[fix] demo script for setup data
2017-04-03 17:56:47 +05:30
Nabin Hait
6f038bc1f3 Validate total advance against grand total considering write off amount 2017-04-03 17:56:05 +05:30
Nabin Hait
9300013acc Validate total advance against grand total considering write off amount 2017-04-03 17:35:58 +05:30
Nabin Hait
8d8cba7faa Validate total advance against grand total considering write off amount 2017-04-03 17:26:32 +05:30
Nabin Hait
2d132e32aa Function name changed in company 2017-04-03 17:26:32 +05:30
Saurabh
2e47654f43 [fix] demo script for setup data 2017-04-03 17:25:55 +05:30
Nabin Hait
9bfa0ab7eb Merge branch 'develop' 2017-04-03 13:18:14 +05:30
Nabin Hait
17ddd8f389 bumped to version 8.0.6 2017-04-03 13:48:13 +06:00
Nabin Hait
c469f2c954 Get POS profile fix 2017-04-03 13:01:11 +05:30
Nabin Hait
731efe3a95 Minor fix in gross profit report 2017-04-03 12:11:36 +05:30
Nabin Hait
2c892c7e7f Indentation fixed in gross profit report 2017-04-03 12:05:35 +05:30
Nabin Hait
e6b5843a1f Merge branch 'develop' 2017-04-01 19:02:55 +05:30
Nabin Hait
31334328ed bumped to version 8.0.5 2017-04-01 19:32:55 +06:00
Rushabh Mehta
0d6db6c42c [fix] add user default role 2017-03-31 23:02:19 +05:30
Nabin Hait
ed0422a8d9 Merge branch 'develop' 2017-03-31 18:19:57 +05:30
Nabin Hait
d991c8666c bumped to version 8.0.4 2017-03-31 18:49:57 +06:00
Nabin Hait
85d1c07519 Merge pull request #8269 from rohitwaghchaure/v8_documentation
[Documentation] Added for custom roles for page and report, fixed some broken images
2017-03-31 18:04:44 +05:30
Nabin Hait
04ab7dfd2f Update role-permisison-for-page-and-report.md 2017-03-31 18:04:15 +05:30
Faris Ansari
eae30e5642 [fix] Show project title in kanban view (#8268)
- frappe/erpnext#8232
2017-03-31 17:46:12 +05:30
Nabin Hait
065302298f Merge pull request #8261 from rohitwaghchaure/pull_project_from_bom_to_po
[enhance] Get project from bom on production order
2017-03-31 17:40:43 +05:30
Nabin Hait
9f84a99158 Merge pull request #8258 from rohitwaghchaure/gross_profit_issue_v_8
[fix] get_valuation_rate takes 4 arguments 3 given in gross profit report
2017-03-31 17:39:58 +05:30
Nabin Hait
b15d999147 Merge branch 'develop' into gross_profit_issue_v_8 2017-03-31 17:39:45 +05:30
Rushabh Mehta
4b0381c820 [fix] tests 2017-03-31 17:36:48 +05:30
Rushabh Mehta
f0b4562a95 [refactor] move purchase_common.js -> public/js/controllers/buying.js 2017-03-31 17:36:48 +05:30
Rushabh Mehta
cc8b2b2fdb [fix] [refactor] demo for v8 and remove purchase common 2017-03-31 17:36:48 +05:30
Rohit Waghchaure
67a3e65f08 [Documentation] Added for custom roles for page and report, fixed some broken images 2017-03-31 17:22:58 +05:30
Rushabh Mehta
dc89916aa9 [fix] salary component fix for abbr 2017-03-31 17:20:57 +05:30
Rohit Waghchaure
e7c0188732 [enhance] Get project from bom on production order 2017-03-31 13:40:06 +05:30
Rohit Waghchaure
34b1dba9aa [fix] get_valuation_rate takes 4 arguments 3 given in gross profit report 2017-03-31 12:44:10 +05:30
Nabin Hait
c040015bb5 Used db_update in patch to bypass validate and on_update method 2017-03-31 11:58:19 +05:30
Rushabh Mehta
b66edd19ca [minor] refactor transaction.js 2017-03-31 10:50:58 +05:30
72 changed files with 497 additions and 511 deletions

View File

@@ -2,7 +2,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
__version__ = '8.0.3' __version__ = '8.0.8'
def get_default_company(user=None): def get_default_company(user=None):
'''Get default company for user''' '''Get default company for user'''
@@ -25,6 +25,14 @@ def get_default_currency():
if company: if company:
return frappe.db.get_value('Company', company, 'default_currency') return frappe.db.get_value('Company', company, 'default_currency')
def get_company_currency(company):
'''Returns the default company currency'''
if not frappe.flags.company_currency:
frappe.flags.company_currency = {}
if not company in frappe.flags.company_currency:
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): def set_perpetual_inventory(enable=1):
accounts_settings = frappe.get_doc("Accounts Settings") accounts_settings = frappe.get_doc("Accounts Settings")
accounts_settings.auto_accounting_for_stock = enable accounts_settings.auto_accounting_for_stock = enable

View File

@@ -2,13 +2,12 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe, erpnext
from frappe import _ from frappe import _
from frappe.utils import flt, fmt_money, getdate, formatdate from frappe.utils import flt, fmt_money, getdate, formatdate
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.accounts.party import validate_party_gle_currency, validate_party_frozen_disabled from erpnext.accounts.party import validate_party_gle_currency, validate_party_frozen_disabled
from erpnext.accounts.utils import get_account_currency from erpnext.accounts.utils import get_account_currency
from erpnext.setup.doctype.company.company import get_company_currency
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year
from erpnext.exceptions import InvalidAccountCurrency from erpnext.exceptions import InvalidAccountCurrency
@@ -19,7 +18,7 @@ class GLEntry(Document):
self.flags.ignore_submit_comment = True self.flags.ignore_submit_comment = True
self.check_mandatory() self.check_mandatory()
self.validate_and_set_fiscal_year() self.validate_and_set_fiscal_year()
if not self.flags.from_repost: if not self.flags.from_repost:
self.pl_must_have_cost_center() self.pl_must_have_cost_center()
self.check_pl_account() self.check_pl_account()
@@ -32,7 +31,7 @@ class GLEntry(Document):
if not from_repost: if not from_repost:
self.validate_account_details(adv_adj) self.validate_account_details(adv_adj)
check_freezing_date(self.posting_date, adv_adj) check_freezing_date(self.posting_date, adv_adj)
validate_frozen_account(self.account, adv_adj) validate_frozen_account(self.account, adv_adj)
validate_balance_type(self.account, adv_adj) validate_balance_type(self.account, adv_adj)
@@ -56,7 +55,7 @@ class GLEntry(Document):
elif account_type == "Payable": elif account_type == "Payable":
frappe.throw(_("{0} {1}: Supplier is required against Payable account {2}") frappe.throw(_("{0} {1}: Supplier is required against Payable account {2}")
.format(self.voucher_type, self.voucher_no, self.account)) .format(self.voucher_type, self.voucher_no, self.account))
# Zero value transaction is not allowed # Zero value transaction is not allowed
if not (flt(self.debit) or flt(self.credit)): if not (flt(self.debit) or flt(self.credit)):
frappe.throw(_("{0} {1}: Either debit or credit amount is required for {2}") frappe.throw(_("{0} {1}: Either debit or credit amount is required for {2}")
@@ -116,7 +115,7 @@ class GLEntry(Document):
validate_party_frozen_disabled(self.party_type, self.party) validate_party_frozen_disabled(self.party_type, self.party)
def validate_currency(self): def validate_currency(self):
company_currency = get_company_currency(self.company) company_currency = erpnext.get_company_currency(self.company)
account_currency = get_account_currency(self.account) account_currency = get_account_currency(self.account)
if not self.account_currency: if not self.account_currency:
@@ -124,7 +123,7 @@ class GLEntry(Document):
if account_currency != self.account_currency: if account_currency != self.account_currency:
frappe.throw(_("{0} {1}: Accounting Entry for {2} can only be made in currency: {3}") frappe.throw(_("{0} {1}: Accounting Entry for {2} can only be made in currency: {3}")
.format(self.voucher_type, self.voucher_no, self.account, .format(self.voucher_type, self.voucher_no, self.account,
(account_currency or company_currency)), InvalidAccountCurrency) (account_currency or company_currency)), InvalidAccountCurrency)
if self.party_type and self.party: if self.party_type and self.party:

View File

@@ -2,12 +2,11 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, json import frappe, erpnext, json
from frappe.utils import cstr, flt, fmt_money, formatdate from frappe.utils import cstr, flt, fmt_money, formatdate
from frappe import msgprint, _, scrub from frappe import msgprint, _, scrub
from erpnext.controllers.accounts_controller import AccountsController from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.utils import get_balance_on, get_account_currency from erpnext.accounts.utils import get_balance_on, get_account_currency
from erpnext.setup.utils import get_company_currency
from erpnext.accounts.party import get_party_account from erpnext.accounts.party import get_party_account
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
from erpnext.hr.doctype.employee_loan.employee_loan import update_disbursement_status from erpnext.hr.doctype.employee_loan.employee_loan import update_disbursement_status
@@ -325,11 +324,11 @@ class JournalEntry(AccountsController):
if d.account_currency == self.company_currency: if d.account_currency == self.company_currency:
d.exchange_rate = 1 d.exchange_rate = 1
elif not d.exchange_rate or d.exchange_rate == 1 or \ elif not d.exchange_rate or d.exchange_rate == 1 or \
(d.reference_type in ("Sales Invoice", "Purchase Invoice") (d.reference_type in ("Sales Invoice", "Purchase Invoice")
and d.reference_name and self.posting_date): and d.reference_name and self.posting_date):
# Modified to include the posting date for which to retreive the exchange rate # Modified to include the posting date for which to retreive the exchange rate
d.exchange_rate = get_exchange_rate(self.posting_date, d.account, d.account_currency, d.exchange_rate = get_exchange_rate(self.posting_date, d.account, d.account_currency,
self.company, d.reference_type, d.reference_name, d.debit, d.credit, d.exchange_rate) self.company, d.reference_type, d.reference_name, d.debit, d.credit, d.exchange_rate)
if not d.exchange_rate: if not d.exchange_rate:
@@ -656,7 +655,7 @@ def get_payment_entry(ref_doc, args):
if args.get("party_account"): if args.get("party_account"):
# Modified to include the posting date for which the exchange rate is required. # Modified to include the posting date for which the exchange rate is required.
# Assumed to be the posting date in the reference document # Assumed to be the posting date in the reference document
exchange_rate = get_exchange_rate(ref_doc.get("posting_date") or ref_doc.get("transaction_date"), exchange_rate = get_exchange_rate(ref_doc.get("posting_date") or ref_doc.get("transaction_date"),
args.get("party_account"), args.get("party_account_currency"), args.get("party_account"), args.get("party_account_currency"),
ref_doc.company, ref_doc.doctype, ref_doc.name) ref_doc.company, ref_doc.doctype, ref_doc.name)
@@ -692,8 +691,8 @@ def get_payment_entry(ref_doc, args):
bank_row.update(bank_account) bank_row.update(bank_account)
# Modified to include the posting date for which the exchange rate is required. # Modified to include the posting date for which the exchange rate is required.
# Assumed to be the posting date of the reference date # Assumed to be the posting date of the reference date
bank_row.exchange_rate = get_exchange_rate(ref_doc.get("posting_date") bank_row.exchange_rate = get_exchange_rate(ref_doc.get("posting_date")
or ref_doc.get("transaction_date"), bank_account["account"], or ref_doc.get("transaction_date"), bank_account["account"],
bank_account["account_currency"], ref_doc.company) bank_account["account_currency"], ref_doc.company)
bank_row.cost_center = cost_center bank_row.cost_center = cost_center
@@ -746,7 +745,7 @@ def get_outstanding(args):
if isinstance(args, basestring): if isinstance(args, basestring):
args = json.loads(args) args = json.loads(args)
company_currency = get_company_currency(args.get("company")) company_currency = erpnext.get_company_currency(args.get("company"))
if args.get("doctype") == "Journal Entry": if args.get("doctype") == "Journal Entry":
condition = " and party=%(party)s" if args.get("party") else "" condition = " and party=%(party)s" if args.get("party") else ""
@@ -805,7 +804,7 @@ def get_account_balance_and_party_type(account, date, company, debit=None, credi
if not frappe.has_permission("Account"): if not frappe.has_permission("Account"):
frappe.msgprint(_("No Permission"), raise_exception=1) frappe.msgprint(_("No Permission"), raise_exception=1)
company_currency = get_company_currency(company) company_currency = erpnext.get_company_currency(company)
account_details = frappe.db.get_value("Account", account, ["account_type", "account_currency"], as_dict=1) account_details = frappe.db.get_value("Account", account, ["account_type", "account_currency"], as_dict=1)
if not account_details: if not account_details:
@@ -853,7 +852,7 @@ def get_exchange_rate(posting_date, account=None, account_currency=None, company
if not account_currency: if not account_currency:
account_currency = account_details.account_currency account_currency = account_details.account_currency
company_currency = get_company_currency(company) company_currency = erpnext.get_company_currency(company)
if account_currency != company_currency: if account_currency != company_currency:
if reference_type in ("Sales Invoice", "Purchase Invoice") and reference_name: if reference_type in ("Sales Invoice", "Purchase Invoice") and reference_name:

View File

@@ -2,8 +2,7 @@
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
frappe.provide("erpnext.accounts"); frappe.provide("erpnext.accounts");
{% include 'erpnext/buying/doctype/purchase_common/purchase_common.js' %}; {% include 'erpnext/public/js/controllers/buying.js' %};
erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
setup: function(doc) { setup: function(doc) {

View File

@@ -2,10 +2,9 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe, erpnext
from frappe.utils import cint, formatdate, flt, getdate from frappe.utils import cint, formatdate, flt, getdate
from frappe import _, throw from frappe import _, throw
from erpnext.setup.utils import get_company_currency
import frappe.defaults import frappe.defaults
from erpnext.controllers.buying_controller import BuyingController from erpnext.controllers.buying_controller import BuyingController
@@ -15,6 +14,7 @@ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_bille
from erpnext.controllers.stock_controller import get_warehouse_account from erpnext.controllers.stock_controller import get_warehouse_account
from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, delete_gl_entries 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.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
from erpnext.buying.utils import check_for_closed_status
form_grid_templates = { form_grid_templates = {
"items": "templates/form_grid/item_grid.html" "items": "templates/form_grid/item_grid.html"
@@ -93,7 +93,7 @@ class PurchaseInvoice(BuyingController):
super(PurchaseInvoice, self).set_missing_values(for_validate) super(PurchaseInvoice, self).set_missing_values(for_validate)
def check_conversion_rate(self): def check_conversion_rate(self):
default_currency = get_company_currency(self.company) default_currency = erpnext.get_company_currency(self.company)
if not default_currency: if not default_currency:
throw(_('Please enter default currency in Company Master')) throw(_('Please enter default currency in Company Master'))
if (self.currency == default_currency and flt(self.conversion_rate) != 1.00) or not self.conversion_rate or (self.currency != default_currency and flt(self.conversion_rate) == 1.00): if (self.currency == default_currency and flt(self.conversion_rate) != 1.00) or not self.conversion_rate or (self.currency != default_currency and flt(self.conversion_rate) == 1.00):
@@ -113,12 +113,11 @@ class PurchaseInvoice(BuyingController):
def check_for_closed_status(self): def check_for_closed_status(self):
check_list = [] check_list = []
pc_obj = frappe.get_doc('Purchase Common')
for d in self.get('items'): for d in self.get('items'):
if d.purchase_order and not d.purchase_order in check_list and not d.purchase_receipt: if d.purchase_order and not d.purchase_order in check_list and not d.purchase_receipt:
check_list.append(d.purchase_order) check_list.append(d.purchase_order)
pc_obj.check_for_closed_status('Purchase Order', d.purchase_order) check_for_closed_status('Purchase Order', d.purchase_order)
def validate_with_previous_doc(self): def validate_with_previous_doc(self):
super(PurchaseInvoice, self).validate_with_previous_doc({ super(PurchaseInvoice, self).validate_with_previous_doc({

View File

@@ -182,6 +182,7 @@ def get_customers_address(customers):
address_data = address[0] address_data = address[0]
address_data.update({'full_name': data.customer_name}) address_data.update({'full_name': data.customer_name})
customer_address[data.name] = address_data customer_address[data.name] = address_data
return customer_address return customer_address
def get_child_nodes(group_type, root): def get_child_nodes(group_type, root):
@@ -337,11 +338,14 @@ def add_customer(name):
def make_address(args, customer): def make_address(args, customer):
if not args.get('address_line1'): return if not args.get('address_line1'): return
name = args.get('name') or get_customers_address(customer)[customer].get("name") name = args.get('name')
if not name:
data = get_customers_address(customer)
name = data[customer].get('name') if data else None
if name: if name:
address = frappe.get_doc('Address', name) address = frappe.get_doc('Address', name)
frappe.errprint(address)
else: else:
address = frappe.new_doc('Address') address = frappe.new_doc('Address')
address.country = frappe.db.get_value('Company', args.get('company'), 'country') address.country = frappe.db.get_value('Company', args.get('company'), 'country')

View File

@@ -4,11 +4,10 @@
# For license information, please see license.txt # For license information, please see license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe, erpnext
from frappe import _, msgprint, throw from frappe import _, msgprint, throw
from frappe.utils import flt, fmt_money from frappe.utils import flt, fmt_money
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.setup.utils import get_company_currency
class OverlappingConditionError(frappe.ValidationError): pass class OverlappingConditionError(frappe.ValidationError): pass
class FromGreaterThanToError(frappe.ValidationError): pass class FromGreaterThanToError(frappe.ValidationError): pass
@@ -77,7 +76,7 @@ class ShippingRule(Document):
overlaps.append([d1, d2]) overlaps.append([d1, d2])
if overlaps: if overlaps:
company_currency = get_company_currency(self.company) company_currency = erpnext.get_company_currency(self.company)
msgprint(_("Overlapping conditions found between:")) msgprint(_("Overlapping conditions found between:"))
messages = [] messages = []
for d1, d2 in overlaps: for d1, d2 in overlaps:

View File

@@ -151,13 +151,6 @@ def set_account_and_due_date(party, account, party_type, company, posting_date,
} }
return out return out
def get_company_currency():
company_currency = frappe._dict()
for d in frappe.get_all("Company", fields=["name", "default_currency"]):
company_currency.setdefault(d.name, d.default_currency)
return company_currency
@frappe.whitelist() @frappe.whitelist()
def get_party_account(party_type, party, company): def get_party_account(party_type, party, company):
"""Returns the account for the given `party`. """Returns the account for the given `party`.

View File

@@ -12,7 +12,7 @@ from frappe.utils import flt
def execute(filters=None): def execute(filters=None):
if not filters: filters = frappe._dict() if not filters: filters = frappe._dict()
company_currency = frappe.db.get_value("Company", filters.company, "default_currency") filters.currency = frappe.db.get_value("Company", filters.company, "default_currency")
gross_profit_data = GrossProfitGenerator(filters) gross_profit_data = GrossProfitGenerator(filters)
@@ -50,7 +50,7 @@ def execute(filters=None):
for col in group_wise_columns.get(scrub(filters.group_by)): for col in group_wise_columns.get(scrub(filters.group_by)):
row.append(src.get(col)) row.append(src.get(col))
row.append(company_currency) row.append(filters.currency)
data.append(row) data.append(row)
return columns, data return columns, data
@@ -218,14 +218,18 @@ class GrossProfitGenerator(object):
def get_average_buying_rate(self, row, item_code): def get_average_buying_rate(self, row, item_code):
if not item_code in self.average_buying_rate: if not item_code in self.average_buying_rate:
if item_code in self.non_stock_items: if item_code in self.non_stock_items:
self.average_buying_rate[item_code] = flt(frappe.db.sql("""select sum(base_net_amount) / sum(qty * conversion_factor) self.average_buying_rate[item_code] = flt(frappe.db.sql("""
select sum(base_net_amount) / sum(qty * conversion_factor)
from `tabPurchase Invoice Item` from `tabPurchase Invoice Item`
where item_code = %s and docstatus=1""", item_code)[0][0]) where item_code = %s and docstatus=1""", item_code)[0][0])
else: else:
average_buying_rate = get_incoming_rate(row) average_buying_rate = get_incoming_rate(row)
if not average_buying_rate: if not average_buying_rate:
average_buying_rate = get_valuation_rate(item_code, row.warehouse, allow_zero_rate=True) average_buying_rate = get_valuation_rate(item_code, row.warehouse,
self.average_buying_rate[item_code] = average_buying_rate row.parenttype, row.parent, allow_zero_rate=True,
currency=self.filters.currency)
self.average_buying_rate[item_code] = flt(average_buying_rate)
return self.average_buying_rate[item_code] return self.average_buying_rate[item_code]
@@ -235,7 +239,7 @@ class GrossProfitGenerator(object):
select (a.base_rate / a.conversion_factor) select (a.base_rate / a.conversion_factor)
from `tabPurchase Invoice Item` a from `tabPurchase Invoice Item` a
where a.item_code = %s and a.docstatus=1 where a.item_code = %s and a.docstatus=1
and modified <= %s and modified <= %s
order by a.modified desc limit 1""", (item_code,self.filters.to_date)) order by a.modified desc limit 1""", (item_code,self.filters.to_date))
else: else:
last_purchase_rate = frappe.db.sql(""" last_purchase_rate = frappe.db.sql("""
@@ -253,7 +257,7 @@ class GrossProfitGenerator(object):
conditions += " and posting_date >= %(from_date)s" conditions += " and posting_date >= %(from_date)s"
if self.filters.to_date: if self.filters.to_date:
conditions += " and posting_date <= %(to_date)s" conditions += " and posting_date <= %(to_date)s"
if self.filters.group_by=="Sales Person": if self.filters.group_by=="Sales Person":
sales_person_cols = ", sales.sales_person, sales.allocated_amount, sales.incentives" sales_person_cols = ", sales.sales_person, sales.allocated_amount, sales.incentives"
sales_team_table = "left join `tabSales Team` sales on sales.parent = `tabSales Invoice`.name" sales_team_table = "left join `tabSales Team` sales on sales.parent = `tabSales Invoice`.name"
@@ -269,7 +273,7 @@ class GrossProfitGenerator(object):
`tabSales Invoice Item`.dn_detail, `tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.stock_qty as qty, `tabSales Invoice Item`.dn_detail, `tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.stock_qty as qty,
`tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount, `tabSales Invoice Item`.name as "item_row" `tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount, `tabSales Invoice Item`.name as "item_row"
{sales_person_cols} {sales_person_cols}
from from
`tabSales Invoice` `tabSales Invoice`
inner join `tabSales Invoice Item` on `tabSales Invoice Item`.parent = `tabSales Invoice`.name inner join `tabSales Invoice Item` on `tabSales Invoice Item`.parent = `tabSales Invoice`.name
{sales_team_table} {sales_team_table}
@@ -277,7 +281,7 @@ class GrossProfitGenerator(object):
`tabSales Invoice`.docstatus = 1 and `tabSales Invoice`.is_return != 1 {conditions} {match_cond} `tabSales Invoice`.docstatus = 1 and `tabSales Invoice`.is_return != 1 {conditions} {match_cond}
order by order by
`tabSales Invoice`.posting_date desc, `tabSales Invoice`.posting_time desc""" `tabSales Invoice`.posting_date desc, `tabSales Invoice`.posting_time desc"""
.format(conditions=conditions, sales_person_cols=sales_person_cols, .format(conditions=conditions, sales_person_cols=sales_person_cols,
sales_team_table=sales_team_table, match_cond = get_match_cond('Sales Invoice')), self.filters, as_dict=1) sales_team_table=sales_team_table, match_cond = get_match_cond('Sales Invoice')), self.filters, as_dict=1)
def load_stock_ledger_entries(self): def load_stock_ledger_entries(self):

View File

@@ -1 +0,0 @@
Common scripts for purchase transactions.

View File

@@ -1 +0,0 @@
from __future__ import unicode_literals

View File

@@ -1,26 +0,0 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"creation": "2012-03-27 14:35:51",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"fields": [],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"modified": "2013-12-20 19:23:27",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Common",
"owner": "Administrator",
"permissions": [],
"read_only": 0,
"read_only_onload": 0
}

View File

@@ -1,105 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe, json
from frappe.utils import flt, cstr, cint
from frappe import _
from erpnext.stock.doctype.item.item import get_last_purchase_details
from erpnext.controllers.buying_controller import BuyingController
class PurchaseCommon(BuyingController):
def update_last_purchase_rate(self, obj, is_submit):
"""updates last_purchase_rate in item table for each item"""
import frappe.utils
this_purchase_date = frappe.utils.getdate(obj.get('posting_date') or obj.get('transaction_date'))
for d in obj.get("items"):
# get last purchase details
last_purchase_details = get_last_purchase_details(d.item_code, obj.name)
# compare last purchase date and this transaction's date
last_purchase_rate = None
if last_purchase_details and \
(last_purchase_details.purchase_date > this_purchase_date):
last_purchase_rate = last_purchase_details['base_rate']
elif is_submit == 1:
# even if this transaction is the latest one, it should be submitted
# for it to be considered for latest purchase rate
if flt(d.conversion_factor):
last_purchase_rate = flt(d.base_rate) / flt(d.conversion_factor)
else:
frappe.throw(_("UOM Conversion factor is required in row {0}").format(d.idx))
# update last purchsae rate
if last_purchase_rate:
frappe.db.sql("""update `tabItem` set last_purchase_rate = %s where name = %s""",
(flt(last_purchase_rate), d.item_code))
def validate_for_items(self, obj):
items = []
for d in obj.get("items"):
if not d.qty:
if obj.doctype == "Purchase Receipt" and d.rejected_qty:
continue
frappe.throw(_("Please enter quantity for Item {0}").format(d.item_code))
# udpate with latest quantities
bin = frappe.db.sql("""select projected_qty from `tabBin` where
item_code = %s and warehouse = %s""", (d.item_code, d.warehouse), as_dict=1)
f_lst ={'projected_qty': bin and flt(bin[0]['projected_qty']) or 0, 'ordered_qty': 0, 'received_qty' : 0}
if d.doctype in ('Purchase Receipt Item', 'Purchase Invoice Item'):
f_lst.pop('received_qty')
for x in f_lst :
if d.meta.get_field(x):
d.set(x, f_lst[x])
item = frappe.db.sql("""select is_stock_item,
is_sub_contracted_item, end_of_life, disabled from `tabItem` where name=%s""",
d.item_code, as_dict=1)[0]
from erpnext.stock.doctype.item.item import validate_end_of_life
validate_end_of_life(d.item_code, item.end_of_life, item.disabled)
# validate stock item
if item.is_stock_item==1 and d.qty and not d.warehouse and not d.delivered_by_supplier:
frappe.throw(_("Warehouse is mandatory for stock Item {0} in row {1}").format(d.item_code, d.idx))
items.append(cstr(d.item_code))
if items and len(items) != len(set(items)) and \
not cint(frappe.db.get_single_value("Buying Settings", "allow_multiple_items") or 0):
frappe.throw(_("Same item cannot be entered multiple times."))
def check_for_closed_status(self, doctype, docname):
status = frappe.db.get_value(doctype, docname, "status")
if status == "Closed":
frappe.throw(_("{0} {1} status is {2}").format(doctype, docname, status), frappe.InvalidStatusError)
@frappe.whitelist()
def get_linked_material_requests(items):
items = json.loads(items)
mr_list = []
for item in items:
material_request = frappe.db.sql("""SELECT distinct mr.name AS mr_name,
(mr_item.qty - mr_item.ordered_qty) AS qty,
mr_item.item_code AS item_code,
mr_item.name AS mr_item
FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
WHERE mr.name = mr_item.parent
AND mr_item.item_code = %(item)s
AND mr.material_request_type = 'Purchase'
AND mr.per_ordered < 99.99
AND mr.docstatus = 1
AND mr.status != 'Stopped'
ORDER BY mr_item.item_code ASC""",{"item": item}, as_dict=1)
if material_request:
mr_list.append(material_request)
return mr_list

View File

@@ -3,7 +3,7 @@
frappe.provide("erpnext.buying"); frappe.provide("erpnext.buying");
{% include 'erpnext/buying/doctype/purchase_common/purchase_common.js' %}; {% include 'erpnext/public/js/controllers/buying.js' %};
frappe.ui.form.on("Purchase Order", { frappe.ui.form.on("Purchase Order", {
setup: function(frm) { setup: function(frm) {

View File

@@ -11,6 +11,8 @@ from erpnext.controllers.buying_controller import BuyingController
from erpnext.stock.doctype.item.item import get_last_purchase_details from erpnext.stock.doctype.item.item import get_last_purchase_details
from erpnext.stock.stock_balance import update_bin_qty, get_ordered_qty from erpnext.stock.stock_balance import update_bin_qty, get_ordered_qty
from frappe.desk.notifications import clear_doctype_notifications from frappe.desk.notifications import clear_doctype_notifications
from erpnext.buying.utils import (validate_for_items, check_for_closed_status,
update_last_purchase_rate)
form_grid_templates = { form_grid_templates = {
@@ -37,9 +39,8 @@ class PurchaseOrder(BuyingController):
super(PurchaseOrder, self).validate() super(PurchaseOrder, self).validate()
self.set_status() self.set_status()
pc_obj = frappe.get_doc('Purchase Common') validate_for_items(self)
pc_obj.validate_for_items(self) self.check_for_closed_status()
self.check_for_closed_status(pc_obj)
self.validate_uom_is_integer("uom", "qty") self.validate_uom_is_integer("uom", "qty")
self.validate_uom_is_integer("stock_uom", ["qty", "required_qty"]) self.validate_uom_is_integer("stock_uom", ["qty", "required_qty"])
@@ -111,12 +112,12 @@ class PurchaseOrder(BuyingController):
= d.rate = item_last_purchase_rate = d.rate = item_last_purchase_rate
# Check for Closed status # Check for Closed status
def check_for_closed_status(self, pc_obj): def check_for_closed_status(self):
check_list =[] check_list =[]
for d in self.get('items'): for d in self.get('items'):
if d.meta.get_field('material_request') and d.material_request and d.material_request not in check_list: if d.meta.get_field('material_request') and d.material_request and d.material_request not in check_list:
check_list.append(d.material_request) check_list.append(d.material_request)
pc_obj.check_for_closed_status('Material Request', d.material_request) check_for_closed_status('Material Request', d.material_request)
def update_requested_qty(self): def update_requested_qty(self):
material_request_map = {} material_request_map = {}
@@ -155,7 +156,7 @@ class PurchaseOrder(BuyingController):
if date_diff and date_diff[0][0]: if date_diff and date_diff[0][0]:
msgprint(_("{0} {1} has been modified. Please refresh.").format(self.doctype, self.name), msgprint(_("{0} {1} has been modified. Please refresh.").format(self.doctype, self.name),
raise_exception=True) raise_exception=True)
def update_status(self, status): def update_status(self, status):
self.check_modified_date() self.check_modified_date()
self.set_status(update=True, status=status) self.set_status(update=True, status=status)
@@ -168,8 +169,6 @@ class PurchaseOrder(BuyingController):
if self.is_against_so(): if self.is_against_so():
self.update_status_updater() self.update_status_updater()
purchase_controller = frappe.get_doc("Purchase Common")
self.update_prevdoc_status() self.update_prevdoc_status()
self.update_requested_qty() self.update_requested_qty()
self.update_ordered_qty() self.update_ordered_qty()
@@ -177,7 +176,7 @@ class PurchaseOrder(BuyingController):
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
self.company, self.base_grand_total) self.company, self.base_grand_total)
purchase_controller.update_last_purchase_rate(self, is_submit = 1) update_last_purchase_rate(self, is_submit = 1)
def on_cancel(self): def on_cancel(self):
if self.is_against_so(): if self.is_against_so():
@@ -186,8 +185,7 @@ class PurchaseOrder(BuyingController):
if self.has_drop_ship_item(): if self.has_drop_ship_item():
self.update_delivered_qty_in_sales_order() self.update_delivered_qty_in_sales_order()
pc_obj = frappe.get_doc('Purchase Common') self.check_for_closed_status()
self.check_for_closed_status(pc_obj)
frappe.db.set(self,'status','Cancelled') frappe.db.set(self,'status','Cancelled')
@@ -197,7 +195,7 @@ class PurchaseOrder(BuyingController):
self.update_requested_qty() self.update_requested_qty()
self.update_ordered_qty() self.update_ordered_qty()
pc_obj.update_last_purchase_rate(self, is_submit = 0) update_last_purchase_rate(self, is_submit = 0)
def on_update(self): def on_update(self):
pass pass
@@ -303,7 +301,7 @@ def make_purchase_invoice(source_name, target_doc=None):
target.amount = flt(obj.amount) - flt(obj.billed_amt) target.amount = flt(obj.amount) - flt(obj.billed_amt)
target.base_amount = target.amount * flt(source_parent.conversion_rate) target.base_amount = target.amount * flt(source_parent.conversion_rate)
target.qty = target.amount / flt(obj.rate) if (flt(obj.rate) and flt(obj.billed_amt)) else flt(obj.qty) target.qty = target.amount / flt(obj.rate) if (flt(obj.rate) and flt(obj.billed_amt)) else flt(obj.qty)
item = frappe.db.get_value("Item", target.item_code, ["item_group", "buying_cost_center"], as_dict=1) item = frappe.db.get_value("Item", target.item_code, ["item_group", "buying_cost_center"], as_dict=1)
target.cost_center = frappe.db.get_value("Project", obj.project, "cost_center") \ target.cost_center = frappe.db.get_value("Project", obj.project, "cost_center") \
or item.buying_cost_center \ or item.buying_cost_center \

View File

@@ -2,7 +2,7 @@
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
{% include 'erpnext/buying/doctype/purchase_common/purchase_common.js' %}; {% include 'erpnext/public/js/controllers/buying.js' %};
cur_frm.add_fetch('contact', 'email_id', 'email_id') cur_frm.add_fetch('contact', 'email_id', 'email_id')

View File

@@ -14,13 +14,14 @@ from frappe.core.doctype.communication.email import make
from erpnext.accounts.party import get_party_account_currency, get_party_details from erpnext.accounts.party import get_party_account_currency, get_party_details
from erpnext.stock.doctype.material_request.material_request import set_missing_values from erpnext.stock.doctype.material_request.material_request import set_missing_values
from erpnext.controllers.buying_controller import BuyingController from erpnext.controllers.buying_controller import BuyingController
from erpnext.buying.utils import validate_for_items
STANDARD_USERS = ("Guest", "Administrator") STANDARD_USERS = ("Guest", "Administrator")
class RequestforQuotation(BuyingController): class RequestforQuotation(BuyingController):
def validate(self): def validate(self):
self.validate_duplicate_supplier() self.validate_duplicate_supplier()
self.validate_common() validate_for_items(self)
self.update_email_id() self.update_email_id()
def validate_duplicate_supplier(self): def validate_duplicate_supplier(self):
@@ -28,10 +29,6 @@ class RequestforQuotation(BuyingController):
if len(supplier_list) != len(set(supplier_list)): if len(supplier_list) != len(set(supplier_list)):
frappe.throw(_("Same supplier has been entered multiple times")) frappe.throw(_("Same supplier has been entered multiple times"))
def validate_common(self):
pc = frappe.get_doc('Purchase Common')
pc.validate_for_items(self)
def update_email_id(self): def update_email_id(self):
for rfq_supplier in self.suppliers: for rfq_supplier in self.suppliers:
if not rfq_supplier.email_id: if not rfq_supplier.email_id:
@@ -130,7 +127,7 @@ class RequestforQuotation(BuyingController):
self.send_email(data, sender, subject, message, attachments) self.send_email(data, sender, subject, message, attachments)
def send_email(self, data, sender, subject, message, attachments): def send_email(self, data, sender, subject, message, attachments):
make(subject = subject, content=message,recipients=data.email_id, make(subject = subject, content=message,recipients=data.email_id,
sender=sender,attachments = attachments, send_email=True, sender=sender,attachments = attachments, send_email=True,
doctype=self.doctype, name=self.name)["name"] doctype=self.doctype, name=self.name)["name"]
@@ -250,26 +247,26 @@ def get_rfq_doc(doctype, name, supplier_idx):
args = doc.get('suppliers')[cint(supplier_idx) - 1] args = doc.get('suppliers')[cint(supplier_idx) - 1]
doc.update_supplier_part_no(args) doc.update_supplier_part_no(args)
return doc return doc
@frappe.whitelist() @frappe.whitelist()
def get_item_from_material_requests_based_on_supplier(source_name, target_doc = None): def get_item_from_material_requests_based_on_supplier(source_name, target_doc = None):
mr_items_list = frappe.db.sql(""" mr_items_list = frappe.db.sql("""
SELECT SELECT
mr.name, mr_item.item_code mr.name, mr_item.item_code
FROM FROM
`tabItem` as item, `tabItem` as item,
`tabItem Supplier` as item_supp, `tabItem Supplier` as item_supp,
`tabMaterial Request Item` as mr_item, `tabMaterial Request Item` as mr_item,
`tabMaterial Request` as mr `tabMaterial Request` as mr
WHERE item_supp.supplier = %(supplier)s WHERE item_supp.supplier = %(supplier)s
AND item.name = item_supp.parent AND item.name = item_supp.parent
AND mr_item.parent = mr.name AND mr_item.parent = mr.name
AND mr_item.item_code = item.name AND mr_item.item_code = item.name
AND mr.status != "Stopped" AND mr.status != "Stopped"
AND mr.material_request_type = "Purchase" AND mr.material_request_type = "Purchase"
AND mr.docstatus = 1 AND mr.docstatus = 1
AND mr.per_ordered < 99.99""", {"supplier": source_name}, as_dict=1) AND mr.per_ordered < 99.99""", {"supplier": source_name}, as_dict=1)
material_requests = {} material_requests = {}
for d in mr_items_list: for d in mr_items_list:
material_requests.setdefault(d.name, []).append(d.item_code) material_requests.setdefault(d.name, []).append(d.item_code)
@@ -293,5 +290,5 @@ def get_item_from_material_requests_based_on_supplier(source_name, target_doc =
] ]
} }
}, target_doc) }, target_doc)
return target_doc return target_doc

View File

@@ -2,7 +2,7 @@
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
// attach required files // attach required files
{% include 'erpnext/buying/doctype/purchase_common/purchase_common.js' %}; {% include 'erpnext/public/js/controllers/buying.js' %};
frappe.ui.form.on('Suppier Quotation', { frappe.ui.form.on('Suppier Quotation', {
setup: function() { setup: function() {

View File

@@ -8,6 +8,7 @@ from frappe.utils import flt
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.buying_controller import BuyingController from erpnext.controllers.buying_controller import BuyingController
from erpnext.buying.utils import validate_for_items
form_grid_templates = { form_grid_templates = {
"items": "templates/form_grid/item_grid.html" "items": "templates/form_grid/item_grid.html"
@@ -24,7 +25,7 @@ class SupplierQuotation(BuyingController):
validate_status(self.status, ["Draft", "Submitted", "Stopped", validate_status(self.status, ["Draft", "Submitted", "Stopped",
"Cancelled"]) "Cancelled"])
self.validate_common() validate_for_items(self)
self.validate_with_previous_doc() self.validate_with_previous_doc()
self.validate_uom_is_integer("uom", "qty") self.validate_uom_is_integer("uom", "qty")
@@ -50,11 +51,6 @@ class SupplierQuotation(BuyingController):
} }
}) })
def validate_common(self):
pc = frappe.get_doc('Purchase Common')
pc.validate_for_items(self)
def get_list_context(context=None): def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context from erpnext.controllers.website_list_for_contact import get_list_context
list_context = get_list_context(context) list_context = get_list_context(context)

80
erpnext/buying/utils.py Normal file
View File

@@ -0,0 +1,80 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe.utils import flt, cstr, cint
from frappe import _
from erpnext.stock.doctype.item.item import get_last_purchase_details
from erpnext.stock.doctype.item.item import validate_end_of_life
def update_last_purchase_rate(doc, is_submit):
"""updates last_purchase_rate in item table for each item"""
import frappe.utils
this_purchase_date = frappe.utils.getdate(doc.get('posting_date') or doc.get('transaction_date'))
for d in doc.get("items"):
# get last purchase details
last_purchase_details = get_last_purchase_details(d.item_code, doc.name)
# compare last purchase date and this transaction's date
last_purchase_rate = None
if last_purchase_details and \
(last_purchase_details.purchase_date > this_purchase_date):
last_purchase_rate = last_purchase_details['base_rate']
elif is_submit == 1:
# even if this transaction is the latest one, it should be submitted
# for it to be considered for latest purchase rate
if flt(d.conversion_factor):
last_purchase_rate = flt(d.base_rate) / flt(d.conversion_factor)
else:
frappe.throw(_("UOM Conversion factor is required in row {0}").format(d.idx))
# update last purchsae rate
if last_purchase_rate:
frappe.db.sql("""update `tabItem` set last_purchase_rate = %s where name = %s""",
(flt(last_purchase_rate), d.item_code))
def validate_for_items(doc):
items = []
for d in doc.get("items"):
if not d.qty:
if doc.doctype == "Purchase Receipt" and d.rejected_qty:
continue
frappe.throw(_("Please enter quantity for Item {0}").format(d.item_code))
# update with latest quantities
bin = frappe.db.sql("""select projected_qty from `tabBin` where
item_code = %s and warehouse = %s""", (d.item_code, d.warehouse), as_dict=1)
f_lst ={'projected_qty': bin and flt(bin[0]['projected_qty']) or 0, 'ordered_qty': 0, 'received_qty' : 0}
if d.doctype in ('Purchase Receipt Item', 'Purchase Invoice Item'):
f_lst.pop('received_qty')
for x in f_lst :
if d.meta.get_field(x):
d.set(x, f_lst[x])
item = frappe.db.sql("""select is_stock_item,
is_sub_contracted_item, end_of_life, disabled from `tabItem` where name=%s""",
d.item_code, as_dict=1)[0]
validate_end_of_life(d.item_code, item.end_of_life, item.disabled)
# validate stock item
if item.is_stock_item==1 and d.qty and not d.warehouse and not d.delivered_by_supplier:
frappe.throw(_("Warehouse is mandatory for stock Item {0} in row {1}").format(d.item_code, d.idx))
items.append(cstr(d.item_code))
if items and len(items) != len(set(items)) and \
not cint(frappe.db.get_single_value("Buying Settings", "allow_multiple_items") or 0):
frappe.throw(_("Same item cannot be entered multiple times."))
def check_for_closed_status(doctype, docname):
status = frappe.db.get_value(doctype, docname, "status")
if status == "Closed":
frappe.throw(_("{0} {1} status is {2}").format(doctype, docname, status), frappe.InvalidStatusError)

View File

@@ -2,10 +2,10 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe, erpnext
from frappe import _, throw from frappe import _, throw
from frappe.utils import today, flt, cint, fmt_money, formatdate, getdate from frappe.utils import today, flt, cint, fmt_money, formatdate, getdate
from erpnext.setup.utils import get_company_currency, get_exchange_rate from erpnext.setup.utils import get_exchange_rate
from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency
from erpnext.utilities.transaction_base import TransactionBase from erpnext.utilities.transaction_base import TransactionBase
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
@@ -22,7 +22,7 @@ class AccountsController(TransactionBase):
@property @property
def company_currency(self): def company_currency(self):
if not hasattr(self, "__company_currency"): if not hasattr(self, "__company_currency"):
self.__company_currency = get_company_currency(self.company) self.__company_currency = erpnext.get_company_currency(self.company)
return self.__company_currency return self.__company_currency

View File

@@ -6,9 +6,10 @@ import frappe
from frappe import _, msgprint from frappe import _, msgprint
from frappe.utils import flt,cint, cstr from frappe.utils import flt,cint, cstr
from erpnext.setup.utils import get_company_currency
from erpnext.accounts.party import get_party_details from erpnext.accounts.party import get_party_details
from erpnext.stock.get_item_details import get_conversion_factor from erpnext.stock.get_item_details import get_conversion_factor
from erpnext.buying.utils import validate_for_items
from erpnext.stock.stock_ledger import get_valuation_rate
from erpnext.controllers.stock_controller import StockController from erpnext.controllers.stock_controller import StockController
@@ -40,9 +41,7 @@ class BuyingController(StockController):
# self.validate_purchase_return() # self.validate_purchase_return()
self.validate_rejected_warehouse() self.validate_rejected_warehouse()
self.validate_accepted_rejected_qty() self.validate_accepted_rejected_qty()
validate_for_items(self)
pc_obj = frappe.get_doc('Purchase Common')
pc_obj.validate_for_items(self)
#sub-contracting #sub-contracting
self.validate_for_subcontracting() self.validate_for_subcontracting()
@@ -88,9 +87,8 @@ class BuyingController(StockController):
def set_total_in_words(self): def set_total_in_words(self):
from frappe.utils import money_in_words from frappe.utils import money_in_words
company_currency = get_company_currency(self.company)
if self.meta.get_field("base_in_words"): if self.meta.get_field("base_in_words"):
self.base_in_words = money_in_words(self.base_grand_total, company_currency) self.base_in_words = money_in_words(self.base_grand_total, self.company_currency)
if self.meta.get_field("in_words"): if self.meta.get_field("in_words"):
self.in_words = money_in_words(self.grand_total, self.currency) self.in_words = money_in_words(self.grand_total, self.currency)
@@ -225,9 +223,8 @@ class BuyingController(StockController):
"serial_no": rm.serial_no "serial_no": rm.serial_no
}) })
if not rm.rate: if not rm.rate:
from erpnext.stock.stock_ledger import get_valuation_rate rm.rate = get_valuation_rate(bom_item.item_code, self.supplier_warehouse,
rm.rate = get_valuation_rate(bom_item.item_code, self.supplier_warehouse, self.doctype, self.name, currency=self.company_currency)
self.doctype, self.name)
else: else:
rm.rate = bom_item.rate rm.rate = bom_item.rate

View File

@@ -106,24 +106,25 @@ def validate_returned_items(doc):
def validate_quantity(doc, args, ref, valid_items, already_returned_items): def validate_quantity(doc, args, ref, valid_items, already_returned_items):
fields = ['qty'] fields = ['qty']
if doc.doctype in ['Purchase Invoice', 'Purchase Receipt']: if doc.doctype in ['Purchase Receipt', 'Purchase Invoice']:
fields.extend(['received_qty', 'rejected_qty']) fields.extend(['received_qty', 'rejected_qty'])
already_returned_data = already_returned_items.get(args.item_code) or {} already_returned_data = already_returned_items.get(args.item_code) or {}
for column in fields: for column in fields:
return_qty = flt(already_returned_data.get(column, 0)) if len(already_returned_data) > 0 else 0 returned_qty = flt(already_returned_data.get(column, 0)) if len(already_returned_data) > 0 else 0
referenced_qty = ref.get(column) reference_qty = ref.get(column)
max_return_qty = flt(referenced_qty) - return_qty max_returnable_qty = flt(reference_qty) - returned_qty
label = column.replace('_', ' ').title() label = column.replace('_', ' ').title()
if reference_qty:
if flt(args.get(column)) > 0: if flt(args.get(column)) > 0:
frappe.throw(_("{0} must be negative in return document").format(label)) frappe.throw(_("{0} must be negative in return document").format(label))
elif return_qty >= referenced_qty and flt(args.get(column)) != 0: elif returned_qty >= reference_qty and args.get(column):
frappe.throw(_("Item {0} has already been returned").format(args.item_code), StockOverReturnError) frappe.throw(_("Item {0} has already been returned")
elif abs(args.get(column)) > max_return_qty: .format(args.item_code), StockOverReturnError)
frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}") elif abs(args.get(column)) > max_returnable_qty:
.format(args.idx, referenced_qty, args.item_code), StockOverReturnError) frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}")
.format(args.idx, reference_qty, args.item_code), StockOverReturnError)
def get_ref_item_dict(valid_items, ref_item_row): def get_ref_item_dict(valid_items, ref_item_row):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos

View File

@@ -4,11 +4,9 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cint, flt, cstr, comma_or from frappe.utils import cint, flt, cstr, comma_or
from erpnext.setup.utils import get_company_currency
from frappe import _, throw from frappe import _, throw
from erpnext.stock.get_item_details import get_bin_details from erpnext.stock.get_item_details import get_bin_details
from erpnext.stock.utils import get_incoming_rate from erpnext.stock.utils import get_incoming_rate
from erpnext.stock.stock_ledger import get_valuation_rate
from erpnext.stock.get_item_details import get_conversion_factor from erpnext.stock.get_item_details import get_conversion_factor
from erpnext.controllers.stock_controller import StockController from erpnext.controllers.stock_controller import StockController
@@ -113,13 +111,11 @@ class SellingController(StockController):
def set_total_in_words(self): def set_total_in_words(self):
from frappe.utils import money_in_words from frappe.utils import money_in_words
company_currency = get_company_currency(self.company)
disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total")) disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total"))
if self.meta.get_field("base_in_words"): if self.meta.get_field("base_in_words"):
self.base_in_words = money_in_words(disable_rounded_total and self.base_in_words = money_in_words(disable_rounded_total and
abs(self.base_grand_total) or abs(self.base_rounded_total), company_currency) abs(self.base_grand_total) or abs(self.base_rounded_total), self.company_currency)
if self.meta.get_field("in_words"): if self.meta.get_field("in_words"):
self.in_words = money_in_words(disable_rounded_total and self.in_words = money_in_words(disable_rounded_total and
abs(self.grand_total) or abs(self.rounded_total), self.currency) abs(self.grand_total) or abs(self.rounded_total), self.currency)
@@ -170,7 +166,7 @@ class SellingController(StockController):
if d.meta.get_field("stock_qty"): if d.meta.get_field("stock_qty"):
if not d.conversion_factor: if not d.conversion_factor:
frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx)) frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx))
d.stock_qty = flt(d.qty) * flt(d.conversion_factor) d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
def validate_selling_price(self): def validate_selling_price(self):
def throw_message(item_name, rate, ref_rate_field): def throw_message(item_name, rate, ref_rate_field):

View File

@@ -54,9 +54,9 @@ class StockController(AccountsController):
self.check_expense_account(item_row) self.check_expense_account(item_row)
# If item is not a sample item # If item is not a sample item
# and ( valuation rate not mentioned in an incoming entry # and ( valuation rate not mentioned in an incoming entry
# or incoming entry not found while delivering the item), # or incoming entry not found while delivering the item),
# try to pick valuation rate from previous sle or Item master and update in SLE # try to pick valuation rate from previous sle or Item master and update in SLE
# Otherwise, throw an exception # Otherwise, throw an exception
@@ -96,25 +96,25 @@ class StockController(AccountsController):
return process_gl_map(gl_list) return process_gl_map(gl_list)
def update_stock_ledger_entries(self, sle): def update_stock_ledger_entries(self, sle):
sle.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse, sle.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
self.doctype, self.name) self.doctype, self.name, currency=self.company_currency)
sle.stock_value = flt(sle.qty_after_transaction) * flt(sle.valuation_rate) sle.stock_value = flt(sle.qty_after_transaction) * flt(sle.valuation_rate)
sle.stock_value_difference = flt(sle.actual_qty) * flt(sle.valuation_rate) sle.stock_value_difference = flt(sle.actual_qty) * flt(sle.valuation_rate)
if sle.name: if sle.name:
frappe.db.sql(""" frappe.db.sql("""
update update
`tabStock Ledger Entry` `tabStock Ledger Entry`
set set
stock_value = %(stock_value)s, stock_value = %(stock_value)s,
valuation_rate = %(valuation_rate)s, valuation_rate = %(valuation_rate)s,
stock_value_difference = %(stock_value_difference)s stock_value_difference = %(stock_value_difference)s
where where
name = %(name)s""", (sle)) name = %(name)s""", (sle))
return sle return sle
def get_voucher_details(self, default_expense_account, default_cost_center, sle_map): def get_voucher_details(self, default_expense_account, default_cost_center, sle_map):
if self.doctype == "Stock Reconciliation": if self.doctype == "Stock Reconciliation":
return [frappe._dict({ "name": voucher_detail_no, "expense_account": default_expense_account, return [frappe._dict({ "name": voucher_detail_no, "expense_account": default_expense_account,
@@ -163,9 +163,9 @@ class StockController(AccountsController):
def get_stock_ledger_details(self): def get_stock_ledger_details(self):
stock_ledger = {} stock_ledger = {}
stock_ledger_entries = frappe.db.sql(""" stock_ledger_entries = frappe.db.sql("""
select select
name, warehouse, stock_value_difference, valuation_rate, name, warehouse, stock_value_difference, valuation_rate,
voucher_detail_no, item_code, posting_date, posting_time, voucher_detail_no, item_code, posting_date, posting_time,
actual_qty, qty_after_transaction actual_qty, qty_after_transaction
from from
`tabStock Ledger Entry` `tabStock Ledger Entry`

View File

@@ -3,10 +3,9 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import json import json
import frappe import frappe, erpnext
from frappe import _, scrub from frappe import _, scrub
from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction
from erpnext.setup.utils import get_company_currency
from erpnext.controllers.accounts_controller import validate_conversion_rate, \ from erpnext.controllers.accounts_controller import validate_conversion_rate, \
validate_taxes_and_charges, validate_inclusive_tax validate_taxes_and_charges, validate_inclusive_tax
@@ -38,7 +37,7 @@ class calculate_taxes_and_totals(object):
def validate_conversion_rate(self): def validate_conversion_rate(self):
# validate conversion rate # validate conversion rate
company_currency = get_company_currency(self.doc.company) company_currency = erpnext.get_company_currency(self.doc.company)
if not self.doc.currency or self.doc.currency == company_currency: if not self.doc.currency or self.doc.currency == company_currency:
self.doc.currency = company_currency self.doc.currency = company_currency
self.doc.conversion_rate = 1.0 self.doc.conversion_rate = 1.0
@@ -327,7 +326,7 @@ class calculate_taxes_and_totals(object):
self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total, self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
self.doc.currency, self.doc.precision("rounded_total")) self.doc.currency, self.doc.precision("rounded_total"))
if self.doc.meta.get_field("base_rounded_total"): if self.doc.meta.get_field("base_rounded_total"):
company_currency = get_company_currency(self.doc.company) company_currency = erpnext.get_company_currency(self.doc.company)
self.doc.base_rounded_total = \ self.doc.base_rounded_total = \
round_based_on_smallest_currency_fraction(self.doc.base_grand_total, round_based_on_smallest_currency_fraction(self.doc.base_grand_total,
@@ -405,11 +404,14 @@ class calculate_taxes_and_totals(object):
self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance")) self.doc.total_advance = flt(total_allocated_amount, self.doc.precision("total_advance"))
if self.doc.party_account_currency == self.doc.currency: if self.doc.party_account_currency == self.doc.currency:
invoice_total = self.doc.grand_total invoice_total = flt(self.doc.grand_total - flt(self.doc.write_off_amount),
else:
invoice_total = flt(self.doc.grand_total * self.doc.conversion_rate,
self.doc.precision("grand_total")) self.doc.precision("grand_total"))
else:
base_write_off_amount = flt(flt(self.doc.write_off_amount) * self.doc.conversion_rate,
self.doc.precision("base_write_off_amount"))
invoice_total = flt(self.doc.grand_total * self.doc.conversion_rate,
self.doc.precision("grand_total")) - base_write_off_amount
if invoice_total > 0 and self.doc.total_advance > invoice_total: if invoice_total > 0 and self.doc.total_advance > invoice_total:
frappe.throw(_("Advance amount cannot be greater than {0} {1}") frappe.throw(_("Advance amount cannot be greater than {0} {1}")
.format(self.doc.party_account_currency, invoice_total)) .format(self.doc.party_account_currency, invoice_total))

View File

@@ -140,16 +140,20 @@ def period_wise_columns_query(filters, trans):
else: else:
trans_date = 'transaction_date' trans_date = 'transaction_date'
qty_field = "t2.stock_qty"
if trans == 'Purchase Invoice':
qty_field = "t2.qty"
if filters.get("period") != 'Yearly': if filters.get("period") != 'Yearly':
for dt in bet_dates: for dt in bet_dates:
get_period_wise_columns(dt, filters.get("period"), pwc) get_period_wise_columns(dt, filters.get("period"), pwc)
query_details = get_period_wise_query(dt, trans_date, query_details) query_details = get_period_wise_query(dt, trans_date, query_details, qty_field)
else: else:
pwc = [_(filters.get("fiscal_year")) + " ("+_("Qty") + "):Float:120", pwc = [_(filters.get("fiscal_year")) + " ("+_("Qty") + "):Float:120",
_(filters.get("fiscal_year")) + " ("+ _("Amt") + "):Currency:120"] _(filters.get("fiscal_year")) + " ("+ _("Amt") + "):Currency:120"]
query_details = " SUM(t2.stock_qty), SUM(t2.base_net_amount)," query_details = " SUM(%s), SUM(t2.base_net_amount),"%(qty_field)
query_details += 'SUM(t2.stock_qty), SUM(t2.base_net_amount)' query_details += 'SUM(%s), SUM(t2.base_net_amount)'%(qty_field)
return pwc, query_details return pwc, query_details
def get_period_wise_columns(bet_dates, period, pwc): def get_period_wise_columns(bet_dates, period, pwc):
@@ -160,10 +164,10 @@ def get_period_wise_columns(bet_dates, period, pwc):
pwc += [_(get_mon(bet_dates[0])) + "-" + _(get_mon(bet_dates[1])) + " (" + _("Qty") + "):Float:120", pwc += [_(get_mon(bet_dates[0])) + "-" + _(get_mon(bet_dates[1])) + " (" + _("Qty") + "):Float:120",
_(get_mon(bet_dates[0])) + "-" + _(get_mon(bet_dates[1])) + " (" + _("Amt") + "):Currency:120"] _(get_mon(bet_dates[0])) + "-" + _(get_mon(bet_dates[1])) + " (" + _("Amt") + "):Currency:120"]
def get_period_wise_query(bet_dates, trans_date, query_details): def get_period_wise_query(bet_dates, trans_date, query_details, qty_field):
query_details += """SUM(IF(t1.%(trans_date)s BETWEEN '%(sd)s' AND '%(ed)s', t2.stock_qty, NULL)), query_details += """SUM(IF(t1.%(trans_date)s BETWEEN '%(sd)s' AND '%(ed)s', %(qty_field)s, NULL)),
SUM(IF(t1.%(trans_date)s BETWEEN '%(sd)s' AND '%(ed)s', t2.base_net_amount, NULL)), SUM(IF(t1.%(trans_date)s BETWEEN '%(sd)s' AND '%(ed)s', t2.base_net_amount, NULL)),
""" % {"trans_date": trans_date, "sd": bet_dates[0],"ed": bet_dates[1]} """ % {"trans_date": trans_date, "sd": bet_dates[0],"ed": bet_dates[1], "qty_field": qty_field}
return query_details return query_details
@frappe.whitelist(allow_guest=True) @frappe.whitelist(allow_guest=True)

View File

@@ -574,7 +574,7 @@
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 1, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Territory", "label": "Territory",
"length": 0, "length": 0,
@@ -1152,7 +1152,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-03-12 21:55:21.598112", "modified": "2017-04-04 01:21:02.165730",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Opportunity", "name": "Opportunity",

View File

@@ -194,11 +194,12 @@ def make_quotation(source_name, target_doc=None):
quotation.transaction_date) quotation.transaction_date)
quotation.conversion_rate = exchange_rate quotation.conversion_rate = exchange_rate
# get default taxes # get default taxes
taxes = get_default_taxes_and_charges("Sales Taxes and Charges Template") taxes = get_default_taxes_and_charges("Sales Taxes and Charges Template")
quotation.extend("taxes", taxes) if taxes:
quotation.extend("taxes", taxes)
quotation.run_method("set_missing_values") quotation.run_method("set_missing_values")
quotation.run_method("calculate_taxes_and_totals") quotation.run_method("calculate_taxes_and_totals")

View File

@@ -63,6 +63,13 @@ def complete_setup(domain='Manufacturing'):
"language": "english" "language": "english"
}) })
company = erpnext.get_default_company()
if company:
company_doc = frappe.get_doc("Company", company)
company_doc.db_set('default_payroll_payable_account',
frappe.db.get_value('Account', dict(account_name='Payroll Payable')))
def setup_demo_page(): def setup_demo_page():
# home page should always be "start" # home page should always be "start"
website_settings = frappe.get_doc("Website Settings", "Website Settings") website_settings = frappe.get_doc("Website Settings", "Website Settings")

View File

@@ -34,14 +34,16 @@ def work():
process_payroll.salary_slip_based_on_timesheet = 0 process_payroll.salary_slip_based_on_timesheet = 0
process_payroll.create_salary_slips() process_payroll.create_salary_slips()
process_payroll.submit_salary_slips() process_payroll.submit_salary_slips()
process_payroll.make_journal_entry(reference_date=frappe.flags.current_date, process_payroll.make_accural_jv_entry()
reference_number=random_string(10)) # process_payroll.make_journal_entry(reference_date=frappe.flags.current_date,
# reference_number=random_string(10))
process_payroll.salary_slip_based_on_timesheet = 1 process_payroll.salary_slip_based_on_timesheet = 1
process_payroll.create_salary_slips() process_payroll.create_salary_slips()
process_payroll.submit_salary_slips() process_payroll.submit_salary_slips()
process_payroll.make_journal_entry(reference_date=frappe.flags.current_date, process_payroll.make_accural_jv_entry()
reference_number=random_string(10)) # process_payroll.make_journal_entry(reference_date=frappe.flags.current_date,
# reference_number=random_string(10))
if frappe.db.get_global('demo_hr_user'): if frappe.db.get_global('demo_hr_user'):
make_timesheet_records() make_timesheet_records()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

Before

Width:  |  Height:  |  Size: 570 KiB

After

Width:  |  Height:  |  Size: 570 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@@ -27,7 +27,7 @@ In ERPNext all Sales and Purchase transactions, like Sales Invoice, Quotation, S
### Customer ### Customer
You can select one of the existing Customer from the Customer master. If Customer doesn't exist in the Customer master, enter Customer Name in the POS Invoice view itself. On creation of POS Invoice, Customer will be auto-created in the Customer master. In POS, user can select the existing customer during making an order or create the new customer. This features works in the offline mode also. User can also add the customer details like contact number, address details etc on the form. The customer which has been created from the POS will be synced when the internet connection is active.
<img class="screenshot" alt="POS Customer" src="{{docs_base_url}}/assets/img/accounts/pos-customer.png"> <img class="screenshot" alt="POS Customer" src="{{docs_base_url}}/assets/img/accounts/pos-customer.png">
@@ -105,4 +105,9 @@ Credits:
To see entries after “Submit”, click on “View Ledger”. To see entries after “Submit”, click on “View Ledger”.
### Email
User can send email from the POS, after submission of an order, user has to click on menu > email
<img class="screenshot" alt="POS Payment" src="{{docs_base_url}}/assets/img/accounts/pos-email.png">
After sync of an order, email sent to the customer with the print of the bill in the attachment
{next} {next}

View File

@@ -1,4 +1,5 @@
adding-users adding-users
role-based-permissions role-based-permissions
user-permissions user-permissions
role-permisison-for-page-and-report
sharing sharing

View File

@@ -0,0 +1,25 @@
# Role Permission for Page and Report
In ERPNext, user can make his custom user interface using Page and the custom report using Report Builder or Query Report. ERPNext has role-based-permission system where user can assign roles to the user. And the same role can be assigned to the page and report, to access them.
If user has enabled the developer mode, then they can add the roles directly in the page and report record. But in that case, the permissions will also be reflected in the json file for the page / report.
### For Page
<img alt="Assign roles to the page" class="screenshot" src="{{docs_base_url}}/assets/img/users-and-permissions/roles-for-page.png">
### For Report
<img alt="Assign roles to the report" class="screenshot" src="{{docs_base_url}}/assets/img/users-and-permissions/roles-for-report.png">
## Tool for custom roles assignment
If developer mode is disabled, then user can assign the roles to the page and report, using "Role Permission for Page and Report" page.
To access, goto Setup > Permissions > Role Permission for Page and Report
<img alt="Tools to assign custom roles to the page" class="screenshot" src="{{docs_base_url}}/assets/img/users-and-permissions/role-permission-for-page-and-report.png">
### Reset to defaults
Using "Reset to Default" button, user can remove the custom permissions applied on a page or report. Then default permissions will be applicable on that page or report.
<img alt="Reset the default roles" class="screenshot" src="{{docs_base_url}}/assets/img/users-and-permissions/reset-roles-permisison-for-page-report.png">

View File

@@ -148,8 +148,8 @@ doc_events = {
"User": { "User": {
"after_insert": "frappe.email.doctype.contact.contact.update_contact", "after_insert": "frappe.email.doctype.contact.contact.update_contact",
"validate": "erpnext.hr.doctype.employee.employee.validate_employee_role", "validate": "erpnext.hr.doctype.employee.employee.validate_employee_role",
"on_update": "erpnext.hr.doctype.employee.employee.update_user_permissions", "on_update": ["erpnext.hr.doctype.employee.employee.update_user_permissions",
"on_update": "frappe.geo.address_and_contact.set_default_role" "erpnext.portal.utils.set_default_role"]
}, },
("Sales Taxes and Charges Template", 'Price List'): { ("Sales Taxes and Charges Template", 'Price List'): {
"on_update": "erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings.validate_cart_settings" "on_update": "erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings.validate_cart_settings"

View File

@@ -4,6 +4,7 @@ frappe.ui.form.on("Employee Attendance Tool", {
}, },
onload: function(frm) { onload: function(frm) {
frm.doc.department = frm.doc.branch = frm.doc.company = "All";
frm.set_value("date", get_today()); frm.set_value("date", get_today());
erpnext.employee_attendance_tool.load_employees(frm); erpnext.employee_attendance_tool.load_employees(frm);
}, },

View File

@@ -13,11 +13,18 @@ class EmployeeAttendanceTool(Document):
@frappe.whitelist() @frappe.whitelist()
def get_employees(date, department=None, branch=None, company=None): def get_employees(date, department = None, branch = None, company = None):
attendance_not_marked = [] attendance_not_marked = []
attendance_marked = [] attendance_marked = []
employee_list = frappe.get_list("Employee", fields=["employee", "employee_name"], filters={ filters = {"status": "Active"}
"status": "Active", "department": department, "branch": branch, "company": company}, order_by="employee_name") if department != "All":
filters["department"] = department
if branch != "All":
filters["branch"] = branch
if company != "All":
filters["company"] = company
employee_list = frappe.get_list("Employee", fields=["employee", "employee_name"], filters=filters, order_by="employee_name")
marked_employee = {} marked_employee = {}
for emp in frappe.get_list("Attendance", fields=["employee", "status"], for emp in frappe.get_list("Attendance", fields=["employee", "status"],
filters={"attendance_date": date}): filters={"attendance_date": date}):

View File

@@ -5,7 +5,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document
from frappe import _ from frappe.model.naming import append_number_if_name_exists
class SalaryComponent(Document): class SalaryComponent(Document):
def validate(self): def validate(self):
@@ -13,12 +13,10 @@ class SalaryComponent(Document):
def validate_abbr(self): def validate_abbr(self):
if not self.salary_component_abbr: if not self.salary_component_abbr:
self.salary_component_abbr = ''.join([c[0] for c in self.salary_component.split()]).upper() self.salary_component_abbr = ''.join([c[0] for c in
self.salary_component.split()]).upper()
self.salary_component_abbr = self.salary_component_abbr.strip() self.salary_component_abbr = self.salary_component_abbr.strip()
if self.get('__islocal') and len(self.salary_component_abbr) > 5: self.salary_component_abbr = append_number_if_name_exists('Salary Component',
frappe.throw(_("Abbreviation cannot have more than 5 characters")) self.salary_component_abbr, 'salary_component_abbr', separator='_')
if frappe.db.sql("select salary_component_abbr from `tabSalary Component` where name!=%s and salary_component_abbr=%s", (self.name, self.salary_component_abbr)):
frappe.throw(_("Abbreviation {0} already used for another salary component").format(self.salary_component_abbr))

View File

@@ -2,13 +2,12 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe, erpnext
from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words
from frappe.model.naming import make_autoname from frappe.model.naming import make_autoname
from frappe import msgprint, _ from frappe import msgprint, _
from erpnext.setup.utils import get_company_currency
from erpnext.hr.doctype.process_payroll.process_payroll import get_start_end_dates from erpnext.hr.doctype.process_payroll.process_payroll import get_start_end_dates
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
from erpnext.utilities.transaction_base import TransactionBase from erpnext.utilities.transaction_base import TransactionBase
@@ -33,7 +32,7 @@ class SalarySlip(TransactionBase):
# if self.salary_slip_based_on_timesheet or not self.net_pay: # if self.salary_slip_based_on_timesheet or not self.net_pay:
self.calculate_net_pay() self.calculate_net_pay()
company_currency = get_company_currency(self.company) company_currency = erpnext.get_company_currency(self.company)
self.total_in_words = money_in_words(self.rounded_total, company_currency) self.total_in_words = money_in_words(self.rounded_total, company_currency)
if frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet"): if frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet"):
@@ -77,7 +76,7 @@ class SalarySlip(TransactionBase):
def eval_condition_and_formula(self, d, data): def eval_condition_and_formula(self, d, data):
try: try:
if d.condition: if d.condition:
if not eval(d.condition, None, data): if not eval(d.condition, {}, data):
return None return None
amount = d.amount amount = d.amount
if d.amount_based_on_formula: if d.amount_based_on_formula:
@@ -348,7 +347,7 @@ class SalarySlip(TransactionBase):
self.sum_components('earnings', 'gross_pay') self.sum_components('earnings', 'gross_pay')
self.sum_components('deductions', 'total_deduction') self.sum_components('deductions', 'total_deduction')
self.set_loan_repayment() self.set_loan_repayment()
self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment)) self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment))
@@ -356,11 +355,11 @@ class SalarySlip(TransactionBase):
self.precision("net_pay") if disable_rounded_total else 0) self.precision("net_pay") if disable_rounded_total else 0)
def set_loan_repayment(self): def set_loan_repayment(self):
employee_loan = frappe.db.sql("""select sum(principal_amount) as principal_amount, sum(interest_amount) as interest_amount, employee_loan = frappe.db.sql("""select sum(principal_amount) as principal_amount, sum(interest_amount) as interest_amount,
sum(total_payment) as total_loan_repayment from `tabRepayment Schedule` sum(total_payment) as total_loan_repayment from `tabRepayment Schedule`
where payment_date between %s and %s and parent in (select name from `tabEmployee Loan` where payment_date between %s and %s and parent in (select name from `tabEmployee Loan`
where employee = %s and repay_from_salary = 1 and docstatus = 1)""", where employee = %s and repay_from_salary = 1 and docstatus = 1)""",
(self.start_date, self.end_date, self.employee), as_dict=True) (self.start_date, self.end_date, self.employee), as_dict=True)
if employee_loan: if employee_loan:
self.principal_amount = employee_loan[0].principal_amount self.principal_amount = employee_loan[0].principal_amount
self.interest_amount = employee_loan[0].interest_amount self.interest_amount = employee_loan[0].interest_amount

View File

@@ -218,7 +218,7 @@ $.extend(cur_frm.cscript, {
project: doc.project project: doc.project
}, },
callback: function(r) { callback: function(r) {
$.each(["description", "stock_uom", "bom_no"], function(i, field) { $.each(["description", "stock_uom", "project", "bom_no"], function(i, field) {
cur_frm.set_value(field, r.message[field]); cur_frm.set_value(field, r.message[field]);
}); });

View File

@@ -498,6 +498,7 @@ def get_item_details(item, project = None):
frappe.throw(_("Default BOM for {0} not found for Project {1}").format(item, project)) frappe.throw(_("Default BOM for {0} not found for Project {1}").format(item, project))
frappe.throw(_("Default BOM for {0} not found").format(item)) frappe.throw(_("Default BOM for {0} not found").format(item))
res['project'] = frappe.db.get_value('BOM', res['bom_no'], 'project')
res.update(check_if_scrap_warehouse_mandatory(res["bom_no"])) res.update(check_if_scrap_warehouse_mandatory(res["bom_no"]))
return res return res

View File

@@ -381,4 +381,5 @@ erpnext.patches.v7_2.move_dates_from_salary_structure_to_employee
erpnext.patches.v7_2.make_all_assessment_group erpnext.patches.v7_2.make_all_assessment_group
erpnext.patches.v8_0.manufacturer_childtable_migrate erpnext.patches.v8_0.manufacturer_childtable_migrate
erpnext.patches.v8_0.repost_reserved_qty_for_multiple_sales_uom erpnext.patches.v8_0.repost_reserved_qty_for_multiple_sales_uom
erpnext.patches.v8_0.addresses_linked_to_lead erpnext.patches.v8_0.addresses_linked_to_lead
execute:frappe.delete_doc('DocType', 'Purchase Common')

View File

@@ -6,20 +6,19 @@ import frappe
def execute(): def execute():
# reading from json and writing it to mariadb # reading from json and writing it to mariadb
# reload_doc needed here with information because new table introduced # reload_doc needed here with information because new table introduced
frappe.reload_doc('stock', 'doctype', 'item_manufacturer') frappe.reload_doc('stock', 'doctype', 'item_manufacturer')
# reload_doctype is a simpler concept of reload_doc # reload_doctype is a simpler concept of reload_doc
frappe.reload_doctype('Item') frappe.reload_doctype('Item')
item_manufacturers = frappe.get_all("Item", fields=["name", "manufacturer", "manufacturer_part_no"]) item_manufacturers = frappe.get_all("Item", fields=["name", "manufacturer", "manufacturer_part_no"])
for item in item_manufacturers: for item in item_manufacturers:
if item.manufacturer or item.manufacturer_part_no: if item.manufacturer or item.manufacturer_part_no:
item_doc = frappe.get_doc("Item", item.name) item_doc = frappe.get_doc("Item", item.name)
item_doc.append("manufacturers", { item_doc.append("manufacturers", {
"manufacturer": item.manufacturer, "manufacturer": item.manufacturer,
"manufacturer_part_no": item.manufacturer_part_no "manufacturer_part_no": item.manufacturer_part_no
}) })
item_doc.flags.ignore_validate = True
item_doc.flags.ignore_mandatory = True item_doc.get("manufacturers")[0].db_update()
item_doc.save()

17
erpnext/portal/utils.py Normal file
View File

@@ -0,0 +1,17 @@
import frappe
def set_default_role(doc, method):
'''Set customer, supplier, student based on email'''
if frappe.flags.setting_role or frappe.flags.in_migrate:
return
contact_name = frappe.get_value('Contact', dict(email_id=doc.email))
if contact_name:
contact = frappe.get_doc('Contact', contact_name)
for link in contact.links:
frappe.flags.setting_role = True
if link.link_doctype=='Customer':
doc.add_roles('Customer')
elif link.link_doctype=='Supplier':
doc.add_roles('Supplier')
elif frappe.get_value('Student', dict(student_email_id=doc.email)):
doc.add_roles('Student')

View File

@@ -1,5 +1,5 @@
frappe.listview_settings['Project'] = { frappe.listview_settings['Project'] = {
add_fields: ["status", "priority", "is_active", "percent_complete", "expected_end_date"], add_fields: ["status", "priority", "is_active", "percent_complete", "expected_end_date", "project_name"],
filters:[["status","=", "Open"]], filters:[["status","=", "Open"]],
get_indicator: function(doc) { get_indicator: function(doc) {
if(doc.status=="Open" && doc.percent_complete) { if(doc.status=="Open" && doc.percent_complete) {

View File

@@ -113,7 +113,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
frappe.model.round_floats_in(item, ["qty", "received_qty"]); frappe.model.round_floats_in(item, ["qty", "received_qty"]);
if(!doc.is_return && this.validate_negative_quantity(cdt, cdn, item, ["qty", "received_qty"])){ return } if(!doc.is_return && this.validate_negative_quantity(cdt, cdn, item, ["qty", "received_qty"])){ return }
if(!item.rejected_qty && item.qty) { if(!item.rejected_qty && item.qty) {
item.received_qty = item.qty; item.received_qty = item.qty;
} }
@@ -138,14 +138,14 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
frappe.model.round_floats_in(item, ["received_qty", "rejected_qty"]); frappe.model.round_floats_in(item, ["received_qty", "rejected_qty"]);
if(!doc.is_return && this.validate_negative_quantity(cdt, cdn, item, ["received_qty", "rejected_qty"])){ return } if(!doc.is_return && this.validate_negative_quantity(cdt, cdn, item, ["received_qty", "rejected_qty"])){ return }
item.qty = flt(item.received_qty - item.rejected_qty, precision("qty", item)); item.qty = flt(item.received_qty - item.rejected_qty, precision("qty", item));
this.qty(doc, cdt, cdn); this.qty(doc, cdt, cdn);
}, },
validate_negative_quantity: function(cdt, cdn, item, fieldnames){ validate_negative_quantity: function(cdt, cdn, item, fieldnames){
if(!item || !fieldnames) { return } if(!item || !fieldnames) { return }
var is_negative_qty = false; var is_negative_qty = false;
for(var i = 0; i<fieldnames.length; i++) { for(var i = 0; i<fieldnames.length; i++) {
if(item[fieldnames[i]] < 0){ if(item[fieldnames[i]] < 0){
@@ -219,12 +219,12 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
my_items.push(cur_frm.doc.items[i].item_code); my_items.push(cur_frm.doc.items[i].item_code);
} }
} }
frappe.call({ frappe.call({
method: "erpnext.buying.doctype.purchase_common.purchase_common.get_linked_material_requests", method: "erpnext.buying.doctype.purchase_common.purchase_common.get_linked_material_requests",
args:{ args:{
items: my_items items: my_items
}, },
callback: function(r) { callback: function(r) {
var i = 0; var i = 0;
var item_length = cur_frm.doc.items.length; var item_length = cur_frm.doc.items.length;
while (i < item_length) { while (i < item_length) {
@@ -239,32 +239,32 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
d.qty = d.qty - my_qty; d.qty = d.qty - my_qty;
cur_frm.doc.items[i].stock_qty = my_qty*cur_frm.doc.items[i].conversion_factor; cur_frm.doc.items[i].stock_qty = my_qty*cur_frm.doc.items[i].conversion_factor;
cur_frm.doc.items[i].qty = my_qty; cur_frm.doc.items[i].qty = my_qty;
frappe.msgprint("Assigning " + d.mr_name + " to " + d.item_code + " (row " + cur_frm.doc.items[i].idx + ")"); frappe.msgprint("Assigning " + d.mr_name + " to " + d.item_code + " (row " + cur_frm.doc.items[i].idx + ")");
if (qty > 0) if (qty > 0)
{ {
frappe.msgprint("Splitting " + qty + " units of " + d.item_code); frappe.msgprint("Splitting " + qty + " units of " + d.item_code);
var newrow = frappe.model.add_child(cur_frm.doc, cur_frm.doc.items[i].doctype, "items"); var newrow = frappe.model.add_child(cur_frm.doc, cur_frm.doc.items[i].doctype, "items");
item_length++; item_length++;
for (key in cur_frm.doc.items[i]) for (key in cur_frm.doc.items[i])
{ {
newrow[key] = cur_frm.doc.items[i][key]; newrow[key] = cur_frm.doc.items[i][key];
} }
newrow.idx = item_length; newrow.idx = item_length;
newrow["stock_qty"] = newrow.conversion_factor*qty; newrow["stock_qty"] = newrow.conversion_factor*qty;
newrow["qty"] = qty; newrow["qty"] = qty;
newrow["material_request"] = ""; newrow["material_request"] = "";
newrow["material_request_item"] = ""; newrow["material_request_item"] = "";
} }
} }
}); });
i++; i++;
} }

View File

@@ -97,7 +97,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}); });
if(this.frm.doc.company && !this.frm.doc.amended_from) { if(this.frm.doc.company && !this.frm.doc.amended_from) {
this.frm.script_manager.trigger("company"); this.frm.trigger("company");
} }
} }
@@ -295,7 +295,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
if (item.serial_no) { if (item.serial_no) {
if (!item.item_code) { if (!item.item_code) {
this.frm.script_manager.trigger("item_code", cdt, cdn); this.frm.trigger("item_code", cdt, cdn);
} }
else { else {
var sr_no = []; var sr_no = [];

View File

@@ -14,9 +14,6 @@ frappe.ui.form.on('Student Batch', {
}); });
}); });
frm.add_custom_button(__("Newsletter"), function() { frm.add_custom_button(__("Newsletter"), function() {
frappe.route_options = {
email_group: frm.doc.name
}
frappe.set_route("List", "Newsletter"); frappe.set_route("List", "Newsletter");
}); });
} }

View File

@@ -26,9 +26,6 @@ frappe.ui.form.on("Student Group", {
}); });
}); });
frm.add_custom_button(__("Newsletter"), function() { frm.add_custom_button(__("Newsletter"), function() {
frappe.route_options = {
email_group: frm.doc.name
}
frappe.set_route("List", "Newsletter"); frappe.set_route("List", "Newsletter");
}); });
} }

View File

@@ -1,3 +1,3 @@
frappe.listview_settings['Customer'] = { frappe.listview_settings['Customer'] = {
add_fields: ["customer_name", "territory", "customer_group", "customer_type"], add_fields: ["customer_name", "territory", "customer_group", "customer_type", "image"],
}; };

View File

@@ -371,7 +371,7 @@
"oldfieldname": "delivery_date", "oldfieldname": "delivery_date",
"oldfieldtype": "Date", "oldfieldtype": "Date",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
@@ -3516,7 +3516,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-03-29 04:38:25.247179", "modified": "2017-03-30 03:23:30.487992",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Order", "name": "Sales Order",

View File

@@ -79,7 +79,7 @@ class Company(Document):
if not frappe.local.flags.ignore_chart_of_accounts: if not frappe.local.flags.ignore_chart_of_accounts:
self.set_default_accounts() self.set_default_accounts()
if self.default_cash_account: if self.default_cash_account:
self.mode_of_payment() self.set_mode_of_payment_account()
if self.default_currency: if self.default_currency:
frappe.db.set_value("Currency", self.default_currency, "enabled", 1) frappe.db.set_value("Currency", self.default_currency, "enabled", 1)
@@ -123,7 +123,7 @@ class Company(Document):
{"company": self.name, "account_type": "Receivable", "is_group": 0})) {"company": self.name, "account_type": "Receivable", "is_group": 0}))
frappe.db.set(self, "default_payable_account", frappe.db.get_value("Account", frappe.db.set(self, "default_payable_account", frappe.db.get_value("Account",
{"company": self.name, "account_type": "Payable", "is_group": 0})) {"company": self.name, "account_type": "Payable", "is_group": 0}))
def validate_coa_input(self): def validate_coa_input(self):
if self.create_chart_of_accounts_based_on == "Existing Company": if self.create_chart_of_accounts_based_on == "Existing Company":
self.chart_of_accounts = None self.chart_of_accounts = None
@@ -166,7 +166,7 @@ class Company(Document):
if account: if account:
self.db_set(fieldname, account) self.db_set(fieldname, account)
def mode_of_payment(self): def set_mode_of_payment_account(self):
cash = frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name') cash = frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name')
if cash and not frappe.db.get_value('Mode of Payment Account', {'company': self.name}): if cash and not frappe.db.get_value('Mode of Payment Account', {'company': self.name}):
mode_of_payment = frappe.get_doc('Mode of Payment', cash) mode_of_payment = frappe.get_doc('Mode of Payment', cash)
@@ -294,7 +294,3 @@ def get_name_with_abbr(name, company):
parts.append(company_abbr) parts.append(company_abbr)
return " - ".join(parts) return " - ".join(parts)
def get_company_currency(company):
return frappe.local_cache("company_currency", company,
lambda: frappe.db.get_value("Company", company, "default_currency"))

View File

@@ -177,6 +177,7 @@ def set_defaults(args):
selling_settings.cust_master_name = "Customer Name" selling_settings.cust_master_name = "Customer Name"
selling_settings.so_required = "No" selling_settings.so_required = "No"
selling_settings.dn_required = "No" selling_settings.dn_required = "No"
selling_settings.allow_multiple_items = 1
selling_settings.save() selling_settings.save()
buying_settings = frappe.get_doc("Buying Settings") buying_settings = frappe.get_doc("Buying Settings")
@@ -184,6 +185,7 @@ def set_defaults(args):
buying_settings.po_required = "No" buying_settings.po_required = "No"
buying_settings.pr_required = "No" buying_settings.pr_required = "No"
buying_settings.maintain_same_rate = 1 buying_settings.maintain_same_rate = 1
buying_settings.allow_multiple_items = 1
buying_settings.save() buying_settings.save()
notification_control = frappe.get_doc("Notification Control") notification_control = frappe.get_doc("Notification Control")

View File

@@ -3,19 +3,10 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _, throw from frappe import _
from frappe.utils import flt from frappe.utils import flt
from frappe.utils import get_datetime_str, nowdate from frappe.utils import get_datetime_str, nowdate
def get_company_currency(company):
currency = frappe.db.get_value("Company", company, "default_currency", cache=True)
if not currency:
currency = frappe.db.get_default("currency")
if not currency:
throw(_('Please specify Default Currency in Company Master and Global Defaults'))
return currency
def get_root_of(doctype): def get_root_of(doctype):
"""Get root element of a DocType with a tree structure""" """Get root element of a DocType with a tree structure"""
result = frappe.db.sql_list("""select name from `tab%s` result = frappe.db.sql_list("""select name from `tab%s`

View File

@@ -287,36 +287,6 @@
"unique": 0, "unique": 0,
"width": "150px" "width": "150px"
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.docstatus==0",
"fieldname": "set_posting_time",
"fieldtype": "Check",
"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": "Edit Posting Date and Time",
"length": 0,
"no_copy": 0,
"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, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -385,6 +355,36 @@
"unique": 0, "unique": 0,
"width": "100px" "width": "100px"
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.docstatus==0",
"fieldname": "set_posting_time",
"fieldtype": "Check",
"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": "Edit Posting Date and Time",
"length": 0,
"no_copy": 0,
"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, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -3288,7 +3288,7 @@
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2017-03-29 04:55:45.044810", "modified": "2017-04-03 03:13:58.051782",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Delivery Note", "name": "Delivery Note",

View File

@@ -11,7 +11,7 @@ frappe.listview_settings['Item'] = {
} else if (doc.end_of_life && doc.end_of_life < frappe.datetime.get_today()) { } else if (doc.end_of_life && doc.end_of_life < frappe.datetime.get_today()) {
return [__("Expired"), "grey", "end_of_life,<,Today"]; return [__("Expired"), "grey", "end_of_life,<,Today"];
} else if (doc.has_variants) { } else if (doc.has_variants) {
return [__("Template"), "blue", "has_variants,=,Yes"]; return [__("Template"), "orange", "has_variants,=,Yes"];
} else if (doc.variant_of) { } else if (doc.variant_of) {
return [__("Variant"), "green", "variant_of,=," + doc.variant_of]; return [__("Variant"), "green", "variant_of,=," + doc.variant_of];
} }

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
{% include 'erpnext/buying/doctype/purchase_common/purchase_common.js' %}; {% include 'erpnext/public/js/controllers/buying.js' %};
frappe.ui.form.on('Material Request', { frappe.ui.form.on('Material Request', {
setup: function(frm) { setup: function(frm) {

View File

@@ -13,7 +13,7 @@ from frappe.model.mapper import get_mapped_doc
from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty
from erpnext.controllers.buying_controller import BuyingController from erpnext.controllers.buying_controller import BuyingController
from erpnext.manufacturing.doctype.production_order.production_order import get_item_details from erpnext.manufacturing.doctype.production_order.production_order import get_item_details
from erpnext.buying.utils import check_for_closed_status, validate_for_items
form_grid_templates = { form_grid_templates = {
"items": "templates/form_grid/material_request_grid.html" "items": "templates/form_grid/material_request_grid.html"
@@ -72,12 +72,9 @@ class MaterialRequest(BuyingController):
from erpnext.controllers.status_updater import validate_status from erpnext.controllers.status_updater import validate_status
validate_status(self.status, ["Draft", "Submitted", "Stopped", "Cancelled"]) validate_status(self.status, ["Draft", "Submitted", "Stopped", "Cancelled"])
pc_obj = frappe.get_doc('Purchase Common') validate_for_items(self)
pc_obj.validate_for_items(self)
# self.set_title() # self.set_title()
# self.validate_qty_against_so() # self.validate_qty_against_so()
# NOTE: Since Item BOM and FG quantities are combined, using current data, it cannot be validated # NOTE: Since Item BOM and FG quantities are combined, using current data, it cannot be validated
# Though the creation of Material Request from a Production Plan can be rethought to fix this # Though the creation of Material Request from a Production Plan can be rethought to fix this
@@ -112,9 +109,7 @@ class MaterialRequest(BuyingController):
self.update_requested_qty() self.update_requested_qty()
def on_cancel(self): def on_cancel(self):
pc_obj = frappe.get_doc('Purchase Common') check_for_closed_status(self.doctype, self.name)
pc_obj.check_for_closed_status(self.doctype, self.name)
self.update_requested_qty() self.update_requested_qty()

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
{% include 'erpnext/buying/doctype/purchase_common/purchase_common.js' %}; {% include 'erpnext/public/js/controllers/buying.js' %};
frappe.provide("erpnext.stock"); frappe.provide("erpnext.stock");

View File

@@ -225,36 +225,6 @@
"unique": 0, "unique": 0,
"width": "50%" "width": "50%"
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.docstatus==0",
"fieldname": "set_posting_time",
"fieldtype": "Check",
"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": "Edit Posting Date and Time",
"length": 0,
"no_copy": 0,
"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, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -323,6 +293,36 @@
"unique": 0, "unique": 0,
"width": "100px" "width": "100px"
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.docstatus==0",
"fieldname": "set_posting_time",
"fieldtype": "Check",
"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": "Edit Posting Date and Time",
"length": 0,
"no_copy": 0,
"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, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -2846,7 +2846,7 @@
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2017-03-14 16:10:58.769483", "modified": "2017-04-05 03:17:55.726121",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Purchase Receipt", "name": "Purchase Receipt",

View File

@@ -12,6 +12,7 @@ from frappe.utils import getdate
from erpnext.controllers.buying_controller import BuyingController from erpnext.controllers.buying_controller import BuyingController
from erpnext.accounts.utils import get_account_currency from erpnext.accounts.utils import get_account_currency
from frappe.desk.notifications import clear_doctype_notifications from frappe.desk.notifications import clear_doctype_notifications
from erpnext.buying.utils import check_for_closed_status, update_last_purchase_rate
form_grid_templates = { form_grid_templates = {
"items": "templates/form_grid/item_grid.html" "items": "templates/form_grid/item_grid.html"
@@ -56,8 +57,7 @@ class PurchaseReceipt(BuyingController):
self.validate_uom_is_integer("uom", ["qty", "received_qty"]) self.validate_uom_is_integer("uom", ["qty", "received_qty"])
self.validate_uom_is_integer("stock_uom", "stock_qty") self.validate_uom_is_integer("stock_uom", "stock_qty")
pc_obj = frappe.get_doc('Purchase Common') self.check_for_closed_status()
self.check_for_closed_status(pc_obj)
if getdate(self.posting_date) > getdate(nowdate()): if getdate(self.posting_date) > getdate(nowdate()):
throw(_("Posting Date cannot be future date")) throw(_("Posting Date cannot be future date"))
@@ -98,17 +98,16 @@ class PurchaseReceipt(BuyingController):
return po_qty, po_warehouse return po_qty, po_warehouse
# Check for Closed status # Check for Closed status
def check_for_closed_status(self, pc_obj): def check_for_closed_status(self):
check_list =[] check_list =[]
for d in self.get('items'): for d in self.get('items'):
if d.meta.get_field('purchase_order') and d.purchase_order and d.purchase_order not in check_list: if (d.meta.get_field('purchase_order') and d.purchase_order
and d.purchase_order not in check_list):
check_list.append(d.purchase_order) check_list.append(d.purchase_order)
pc_obj.check_for_closed_status('Purchase Order', d.purchase_order) check_for_closed_status('Purchase Order', d.purchase_order)
# on submit # on submit
def on_submit(self): def on_submit(self):
purchase_controller = frappe.get_doc("Purchase Common")
# Check for Approving Authority # Check for Approving Authority
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
self.company, self.base_grand_total) self.company, self.base_grand_total)
@@ -120,7 +119,7 @@ class PurchaseReceipt(BuyingController):
self.update_billing_status() self.update_billing_status()
if not self.is_return: if not self.is_return:
purchase_controller.update_last_purchase_rate(self, 1) update_last_purchase_rate(self, 1)
# Updating stock ledger should always be called after updating prevdoc status, # Updating stock ledger should always be called after updating prevdoc status,
# because updating ordered qty in bin depends upon updated ordered qty in PO # because updating ordered qty in bin depends upon updated ordered qty in PO
@@ -140,9 +139,7 @@ class PurchaseReceipt(BuyingController):
frappe.throw(_("Purchase Invoice {0} is already submitted").format(self.submit_rv[0][0])) frappe.throw(_("Purchase Invoice {0} is already submitted").format(self.submit_rv[0][0]))
def on_cancel(self): def on_cancel(self):
pc_obj = frappe.get_doc('Purchase Common') self.check_for_closed_status()
self.check_for_closed_status(pc_obj)
# Check if Purchase Invoice has been submitted against current Purchase Order # Check if Purchase Invoice has been submitted against current Purchase Order
submitted = frappe.db.sql("""select t1.name submitted = frappe.db.sql("""select t1.name
from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2 from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2
@@ -157,7 +154,7 @@ class PurchaseReceipt(BuyingController):
self.update_billing_status() self.update_billing_status()
if not self.is_return: if not self.is_return:
pc_obj.update_last_purchase_rate(self, 0) update_last_purchase_rate(self, 0)
# Updating stock ledger should always be called after updating prevdoc status, # Updating stock ledger should always be called after updating prevdoc status,
# because updating ordered qty in bin depends upon updated ordered qty in PO # because updating ordered qty in bin depends upon updated ordered qty in PO
@@ -170,9 +167,6 @@ class PurchaseReceipt(BuyingController):
bin = frappe.db.sql("select actual_qty from `tabBin` where item_code = %s and warehouse = %s", (d.rm_item_code, self.supplier_warehouse), as_dict = 1) bin = frappe.db.sql("select actual_qty from `tabBin` where item_code = %s and warehouse = %s", (d.rm_item_code, self.supplier_warehouse), as_dict = 1)
d.current_stock = bin and flt(bin[0]['actual_qty']) or 0 d.current_stock = bin and flt(bin[0]['actual_qty']) or 0
def get_rate(self,arg):
return frappe.get_doc('Purchase Common').get_rate(arg,self)
def get_gl_entries(self, warehouse_account=None): def get_gl_entries(self, warehouse_account=None):
from erpnext.accounts.general_ledger import process_gl_map from erpnext.accounts.general_ledger import process_gl_map

View File

@@ -383,36 +383,6 @@
"unique": 0, "unique": 0,
"width": "50%" "width": "50%"
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.docstatus==0",
"fieldname": "set_posting_time",
"fieldtype": "Check",
"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": "Edit Posting Date and Time",
"length": 0,
"no_copy": 0,
"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, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -476,6 +446,36 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.docstatus==0",
"fieldname": "set_posting_time",
"fieldtype": "Check",
"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": "Edit Posting Date and Time",
"length": 0,
"no_copy": 0,
"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, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -1654,7 +1654,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-03-14 16:11:11.741704", "modified": "2017-04-05 03:18:05.437204",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Stock Entry", "name": "Stock Entry",

View File

@@ -327,11 +327,10 @@ def get_pos_profile_item_details(company, args, pos_profile=None):
@frappe.whitelist() @frappe.whitelist()
def get_pos_profile(company): def get_pos_profile(company):
condition = "and company = '%s'"%(company) if company else ''
pos_profile = frappe.db.sql("""select * from `tabPOS Profile` where user = %s pos_profile = frappe.db.sql("""select * from `tabPOS Profile` where user = %s
{cond}""".format(cond=condition), (frappe.session['user']), as_dict=1) and company = %s""", (frappe.session['user'], company), as_dict=1)
if not pos_profile and company: if not pos_profile:
pos_profile = frappe.db.sql("""select * from `tabPOS Profile` pos_profile = frappe.db.sql("""select * from `tabPOS Profile`
where ifnull(user,'') = '' and company = %s""", company, as_dict=1) where ifnull(user,'') = '' and company = %s""", company, as_dict=1)

View File

@@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe, erpnext
from frappe import _ from frappe import _
from frappe.utils import cint, flt, cstr, now from frappe.utils import cint, flt, cstr, now
from erpnext.stock.utils import get_valuation_method from erpnext.stock.utils import get_valuation_method
@@ -258,14 +258,15 @@ class update_entries_after(object):
if not self.valuation_rate and actual_qty > 0: if not self.valuation_rate and actual_qty > 0:
self.valuation_rate = sle.incoming_rate self.valuation_rate = sle.incoming_rate
# Get valuation rate from previous SLE or Item master, if item is not a sample item # Get valuation rate from previous SLE or Item master, if item is not a sample item
if not self.valuation_rate and sle.voucher_detail_no: if not self.valuation_rate and sle.voucher_detail_no:
is_sample_item = self.check_if_sample_item(sle.voucher_type, sle.voucher_detail_no) is_sample_item = self.check_if_sample_item(sle.voucher_type, sle.voucher_detail_no)
if not is_sample_item: if not is_sample_item:
self.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse, self.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
sle.voucher_type, sle.voucher_no, self.allow_zero_rate) sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
currency=erpnext.get_company_currency(sle.company))
def get_fifo_values(self, sle): def get_fifo_values(self, sle):
incoming_rate = flt(sle.incoming_rate) incoming_rate = flt(sle.incoming_rate)
actual_qty = flt(sle.actual_qty) actual_qty = flt(sle.actual_qty)
@@ -291,11 +292,12 @@ class update_entries_after(object):
# Get valuation rate from last sle if exists or from valuation rate field in item master # Get valuation rate from last sle if exists or from valuation rate field in item master
is_sample_item = self.check_if_sample_item(sle.voucher_type, sle.voucher_detail_no) is_sample_item = self.check_if_sample_item(sle.voucher_type, sle.voucher_detail_no)
if not is_sample_item: if not is_sample_item:
_rate = get_valuation_rate(sle.item_code, sle.warehouse, _rate = get_valuation_rate(sle.item_code, sle.warehouse,
sle.voucher_type, sle.voucher_no, self.allow_zero_rate) sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
currency=erpnext.get_company_currency(sle.company))
else: else:
_rate = 0 _rate = 0
self.stock_queue.append([0, _rate]) self.stock_queue.append([0, _rate])
index = None index = None
@@ -341,11 +343,11 @@ class update_entries_after(object):
if not self.stock_queue: if not self.stock_queue:
self.stock_queue.append([0, sle.incoming_rate or sle.outgoing_rate or self.valuation_rate]) self.stock_queue.append([0, sle.incoming_rate or sle.outgoing_rate or self.valuation_rate])
def check_if_sample_item(self, voucher_type, voucher_detail_no): def check_if_sample_item(self, voucher_type, voucher_detail_no):
ref_item_dt = voucher_type + (" Detail" if voucher_type == "Stock Entry" else " Item") ref_item_dt = voucher_type + (" Detail" if voucher_type == "Stock Entry" else " Item")
return frappe.db.get_value(ref_item_dt, voucher_detail_no, "is_sample_item") return frappe.db.get_value(ref_item_dt, voucher_detail_no, "is_sample_item")
def get_sle_before_datetime(self): def get_sle_before_datetime(self):
"""get previous stock ledger entry before current time-bucket""" """get previous stock ledger entry before current time-bucket"""
return get_stock_ledger_entries(self.args, "<", "desc", "limit 1", for_update=False) return get_stock_ledger_entries(self.args, "<", "desc", "limit 1", for_update=False)
@@ -419,7 +421,8 @@ def get_stock_ledger_entries(previous_sle, operator=None, order="desc", limit=No
"order": order "order": order
}, previous_sle, as_dict=1, debug=debug) }, previous_sle, as_dict=1, debug=debug)
def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no, allow_zero_rate=False): def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
allow_zero_rate=False, currency=None):
# Get valuation rate from last sle for the same item and warehouse # Get valuation rate from last sle for the same item and warehouse
last_valuation_rate = frappe.db.sql("""select valuation_rate last_valuation_rate = frappe.db.sql("""select valuation_rate
from `tabStock Ledger Entry` from `tabStock Ledger Entry`
@@ -441,6 +444,11 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no, allow_zer
# syste does not found any SLE, then take valuation rate from Item # syste does not found any SLE, then take valuation rate from Item
valuation_rate = frappe.db.get_value("Item", item_code, "valuation_rate") valuation_rate = frappe.db.get_value("Item", item_code, "valuation_rate")
if not valuation_rate:
# try in price list
valuation_rate = frappe.db.get_value('Item Price',
dict(item_code=item_code, buying=1, currency=currency), 'price_list_rate')
if not allow_zero_rate and not valuation_rate \ if not allow_zero_rate and not valuation_rate \
and cint(frappe.db.get_value("Accounts Settings", None, "auto_accounting_for_stock")): and cint(frappe.db.get_value("Accounts Settings", None, "auto_accounting_for_stock")):
frappe.local.message_log = [] frappe.local.message_log = []

View File

@@ -1,5 +1,6 @@
{ {
"allow_copy": 0, "allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1, "allow_import": 1,
"allow_rename": 1, "allow_rename": 1,
"autoname": "naming_series:", "autoname": "naming_series:",
@@ -285,7 +286,7 @@
"columns": 0, "columns": 0,
"depends_on": "", "depends_on": "",
"fieldname": "description", "fieldname": "description",
"fieldtype": "Text", "fieldtype": "Text Editor",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
@@ -607,7 +608,7 @@
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fieldname": "resolution_details", "fieldname": "resolution_details",
"fieldtype": "Small Text", "fieldtype": "Text Editor",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
@@ -808,18 +809,18 @@
"unique": 0 "unique": 0
} }
], ],
"has_web_view": 0,
"hide_heading": 0, "hide_heading": 0,
"hide_toolbar": 0, "hide_toolbar": 0,
"icon": "fa fa-ticket", "icon": "fa fa-ticket",
"idx": 7, "idx": 7,
"image_view": 0, "image_view": 0,
"in_create": 0, "in_create": 0,
"in_dialog": 0,
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-02-20 13:32:31.898423", "modified": "2017-04-05 21:42:23.740187",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Support", "module": "Support",
"name": "Issue", "name": "Issue",