[fix] [refactor] demo for v8 and remove purchase common

This commit is contained in:
Rushabh Mehta
2017-03-31 12:44:29 +05:30
committed by Nabin Hait
parent dc89916aa9
commit cc8b2b2fdb
32 changed files with 233 additions and 683 deletions

View File

@@ -25,6 +25,14 @@ def get_default_currency():
if company:
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):
accounts_settings = frappe.get_doc("Accounts Settings")
accounts_settings.auto_accounting_for_stock = enable

View File

@@ -2,13 +2,12 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
import frappe, erpnext
from frappe import _
from frappe.utils import flt, fmt_money, getdate, formatdate
from frappe.model.document import Document
from erpnext.accounts.party import validate_party_gle_currency, validate_party_frozen_disabled
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.exceptions import InvalidAccountCurrency
@@ -116,7 +115,7 @@ class GLEntry(Document):
validate_party_frozen_disabled(self.party_type, self.party)
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)
if not self.account_currency:

View File

@@ -2,12 +2,11 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe, json
import frappe, erpnext, json
from frappe.utils import cstr, flt, fmt_money, formatdate
from frappe import msgprint, _, scrub
from erpnext.controllers.accounts_controller import AccountsController
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.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
from erpnext.hr.doctype.employee_loan.employee_loan import update_disbursement_status
@@ -746,7 +745,7 @@ def get_outstanding(args):
if isinstance(args, basestring):
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":
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"):
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)
if not account_details:
@@ -853,7 +852,7 @@ def get_exchange_rate(posting_date, account=None, account_currency=None, company
if not 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 reference_type in ("Sales Invoice", "Purchase Invoice") and reference_name:

View File

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

View File

@@ -4,11 +4,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
import frappe, erpnext
from frappe import _, msgprint, throw
from frappe.utils import flt, fmt_money
from frappe.model.document import Document
from erpnext.setup.utils import get_company_currency
class OverlappingConditionError(frappe.ValidationError): pass
class FromGreaterThanToError(frappe.ValidationError): pass
@@ -77,7 +76,7 @@ class ShippingRule(Document):
overlaps.append([d1, d2])
if overlaps:
company_currency = get_company_currency(self.company)
company_currency = erpnext.get_company_currency(self.company)
msgprint(_("Overlapping conditions found between:"))
messages = []
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
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()
def get_party_account(party_type, party, company):
"""Returns the account for the given `party`.

View File

@@ -12,7 +12,7 @@ from frappe.utils import flt
def execute(filters=None):
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)
@@ -50,7 +50,7 @@ def execute(filters=None):
for col in group_wise_columns.get(scrub(filters.group_by)):
row.append(src.get(col))
row.append(company_currency)
row.append(filters.currency)
data.append(row)
return columns, data
@@ -224,7 +224,8 @@ class GrossProfitGenerator(object):
else:
average_buying_rate = get_incoming_rate(row)
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,
allow_zero_rate=True, currency=self.filters.currency)
self.average_buying_rate[item_code] = average_buying_rate
return self.average_buying_rate[item_code]

View File

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

View File

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

View File

@@ -1,372 +0,0 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.provide("erpnext.buying");
cur_frm.cscript.tax_table = "Purchase Taxes and Charges";
{% include 'erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.js' %}
cur_frm.email_field = "contact_email";
erpnext.buying.BuyingController = erpnext.TransactionController.extend({
setup: function() {
this._super();
},
onload: function() {
this.setup_queries();
this._super();
if(this.frm.get_field('shipping_address')) {
this.frm.set_query("shipping_address", function(){
if(me.frm.doc.customer){
return {
query: 'frappe.geo.doctype.address.address.address_query',
filters: { link_doctype: 'Customer', link_name: me.frm.doc.customer }
};
} else
return erpnext.queries.company_address_query(me.frm.doc)
});
}
},
setup_queries: function() {
var me = this;
if(this.frm.fields_dict.buying_price_list) {
this.frm.set_query("buying_price_list", function() {
return{
filters: { 'buying': 1 }
}
});
}
me.frm.set_query('supplier', erpnext.queries.supplier);
me.frm.set_query('contact_person', erpnext.queries.contact_query);
me.frm.set_query('supplier_address', erpnext.queries.address_query);
if(this.frm.fields_dict.supplier) {
this.frm.set_query("supplier", function() {
return{ query: "erpnext.controllers.queries.supplier_query" }});
}
this.frm.set_query("item_code", "items", function() {
if(me.frm.doc.is_subcontracted == "Yes") {
return{
query: "erpnext.controllers.queries.item_query",
filters:{ 'is_sub_contracted_item': 1 }
}
} else {
return{
query: "erpnext.controllers.queries.item_query",
filters: {'is_purchase_item': 1}
}
}
});
},
refresh: function(doc) {
frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'supplier', doctype: 'Supplier'};
this.frm.toggle_display("supplier_name",
(this.frm.doc.supplier_name && this.frm.doc.supplier_name!==this.frm.doc.supplier));
if(this.frm.doc.docstatus==0 &&
(this.frm.doctype==="Purchase Order" || this.frm.doctype==="Material Request")) {
this.set_from_product_bundle();
}
this._super();
},
supplier: function() {
var me = this;
erpnext.utils.get_party_details(this.frm, null, null, function(){me.apply_pricing_rule()});
},
supplier_address: function() {
erpnext.utils.get_address_display(this.frm);
},
buying_price_list: function() {
this.apply_price_list();
},
price_list_rate: function(doc, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
frappe.model.round_floats_in(item, ["price_list_rate", "discount_percentage"]);
item.rate = flt(item.price_list_rate * (1 - item.discount_percentage / 100.0),
precision("rate", item));
this.calculate_taxes_and_totals();
},
discount_percentage: function(doc, cdt, cdn) {
this.price_list_rate(doc, cdt, cdn);
},
qty: function(doc, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
if ((doc.doctype == "Purchase Receipt") || (doc.doctype == "Purchase Invoice" && (doc.update_stock || doc.is_return))) {
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(!item.rejected_qty && item.qty) {
item.received_qty = item.qty;
}
frappe.model.round_floats_in(item, ["qty", "received_qty"]);
item.rejected_qty = flt(item.received_qty - item.qty, precision("rejected_qty", item));
}
this._super(doc, cdt, cdn);
},
received_qty: function(doc, cdt, cdn) {
this.calculate_accepted_qty(doc, cdt, cdn)
},
rejected_qty: function(doc, cdt, cdn) {
this.calculate_accepted_qty(doc, cdt, cdn)
},
calculate_accepted_qty: function(doc, cdt, cdn){
var item = frappe.get_doc(cdt, cdn);
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 }
item.qty = flt(item.received_qty - item.rejected_qty, precision("qty", item));
this.qty(doc, cdt, cdn);
},
validate_negative_quantity: function(cdt, cdn, item, fieldnames){
if(!item || !fieldnames) { return }
var is_negative_qty = false;
for(var i = 0; i<fieldnames.length; i++) {
if(item[fieldnames[i]] < 0){
frappe.msgprint(__("Row #{0}: {1} can not be negative for item {2}",
[item.idx,__(frappe.meta.get_label(cdt, fieldnames[i], cdn)), item.item_code]));
is_negative_qty = true;
break;
}
}
return is_negative_qty
},
warehouse: function(doc, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
if(item.item_code && item.warehouse) {
return this.frm.call({
method: "erpnext.stock.get_item_details.get_bin_details",
child: item,
args: {
item_code: item.item_code,
warehouse: item.warehouse
}
});
}
},
project: function(doc, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
if(item.project) {
$.each(this.frm.doc["items"] || [],
function(i, other_item) {
if(!other_item.project) {
other_item.project = item.project;
refresh_field("project", other_item.name, other_item.parentfield);
}
});
}
},
category: function(doc, cdt, cdn) {
// should be the category field of tax table
if(cdt != doc.doctype) {
this.calculate_taxes_and_totals();
}
},
add_deduct_tax: function(doc, cdt, cdn) {
this.calculate_taxes_and_totals();
},
set_from_product_bundle: function() {
var me = this;
this.frm.add_custom_button(__("Product Bundle"), function() {
erpnext.buying.get_items_from_product_bundle(me.frm);
}, __("Get items from"));
},
shipping_address: function(){
var me = this;
erpnext.utils.get_address_display(this.frm, "shipping_address",
"shipping_address_display", is_your_company_address=true)
},
tc_name: function() {
this.get_terms();
},
link_to_mrs: function() {
my_items = [];
for (var i in cur_frm.doc.items) {
if(!cur_frm.doc.items[i].material_request){
my_items.push(cur_frm.doc.items[i].item_code);
}
}
frappe.call({
method: "erpnext.buying.doctype.purchase_common.purchase_common.get_linked_material_requests",
args:{
items: my_items
},
callback: function(r) {
var i = 0;
var item_length = cur_frm.doc.items.length;
while (i < item_length) {
var qty = cur_frm.doc.items[i].qty;
(r.message[0] || []).forEach(function(d) {
if (d.qty > 0 && qty > 0 && cur_frm.doc.items[i].item_code == d.item_code && !cur_frm.doc.items[i].material_request_item)
{
cur_frm.doc.items[i].material_request = d.mr_name;
cur_frm.doc.items[i].material_request_item = d.mr_item;
my_qty = Math.min(qty, d.qty);
qty = 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].qty = my_qty;
frappe.msgprint("Assigning " + d.mr_name + " to " + d.item_code + " (row " + cur_frm.doc.items[i].idx + ")");
if (qty > 0)
{
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");
item_length++;
for (key in cur_frm.doc.items[i])
{
newrow[key] = cur_frm.doc.items[i][key];
}
newrow.idx = item_length;
newrow["stock_qty"] = newrow.conversion_factor*qty;
newrow["qty"] = qty;
newrow["material_request"] = "";
newrow["material_request_item"] = "";
}
}
});
i++;
}
refresh_field("items");
//cur_frm.save();
}
});
}
});
cur_frm.add_fetch('project', 'cost_center', 'cost_center');
erpnext.buying.get_default_bom = function(frm) {
$.each(frm.doc["items"] || [], function(i, d) {
if (d.item_code && d.bom === "") {
return frappe.call({
type: "GET",
method: "erpnext.stock.get_item_details.get_default_bom",
args: {
"item_code": d.item_code,
},
callback: function(r) {
if(r) {
frappe.model.set_value(d.doctype, d.name, "bom", r.message);
}
}
})
}
});
}
erpnext.buying.get_items_from_product_bundle = function(frm) {
var dialog = new frappe.ui.Dialog({
title: __("Get Items from Product Bundle"),
fields: [
{
"fieldtype": "Link",
"label": __("Product Bundle"),
"fieldname": "product_bundle",
"options":"Product Bundle",
"reqd": 1
},
{
"fieldtype": "Currency",
"label": __("Quantity"),
"fieldname": "quantity",
"reqd": 1,
"default": 1
},
{
"fieldtype": "Button",
"label": __("Get Items"),
"fieldname": "get_items",
"cssClass": "btn-primary"
}
]
});
dialog.fields_dict.get_items.$input.click(function() {
args = dialog.get_values();
if(!args) return;
dialog.hide();
return frappe.call({
type: "GET",
method: "erpnext.stock.doctype.packed_item.packed_item.get_items_from_product_bundle",
args: {
args: {
item_code: args.product_bundle,
quantity: args.quantity,
parenttype: frm.doc.doctype,
parent: frm.doc.name,
supplier: frm.doc.supplier,
currency: frm.doc.currency,
conversion_rate: frm.doc.conversion_rate,
price_list: frm.doc.buying_price_list,
price_list_currency: frm.doc.price_list_currency,
plc_conversion_rate: frm.doc.plc_conversion_rate,
company: frm.doc.company,
is_subcontracted: frm.doc.is_subcontracted,
transaction_date: frm.doc.transaction_date || frm.doc.posting_date,
ignore_pricing_rule: frm.doc.ignore_pricing_rule
}
},
freeze: true,
callback: function(r) {
if(!r.exc && r.message) {
for ( var i=0; i< r.message.length; i++ ) {
var d = frm.add_child("items");
var item = r.message[i];
for ( var key in item) {
if ( !is_null(item[key]) ) {
d[key] = item[key];
}
}
if(frappe.meta.get_docfield(d.doctype, "price_list_rate", d.name)) {
frm.script_manager.trigger("price_list_rate", d.doctype, d.name);
}
}
frm.refresh_field("items");
}
}
})
});
dialog.show();
}

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

@@ -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.stock_balance import update_bin_qty, get_ordered_qty
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 = {
@@ -37,9 +39,8 @@ class PurchaseOrder(BuyingController):
super(PurchaseOrder, self).validate()
self.set_status()
pc_obj = frappe.get_doc('Purchase Common')
pc_obj.validate_for_items(self)
self.check_for_closed_status(pc_obj)
validate_for_items(self)
self.check_for_closed_status()
self.validate_uom_is_integer("uom", "qty")
self.validate_uom_is_integer("stock_uom", ["qty", "required_qty"])
@@ -111,12 +112,12 @@ class PurchaseOrder(BuyingController):
= d.rate = item_last_purchase_rate
# Check for Closed status
def check_for_closed_status(self, pc_obj):
def check_for_closed_status(self):
check_list =[]
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:
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):
material_request_map = {}
@@ -168,8 +169,6 @@ class PurchaseOrder(BuyingController):
if self.is_against_so():
self.update_status_updater()
purchase_controller = frappe.get_doc("Purchase Common")
self.update_prevdoc_status()
self.update_requested_qty()
self.update_ordered_qty()
@@ -177,7 +176,7 @@ class PurchaseOrder(BuyingController):
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
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):
if self.is_against_so():
@@ -186,8 +185,7 @@ class PurchaseOrder(BuyingController):
if self.has_drop_ship_item():
self.update_delivered_qty_in_sales_order()
pc_obj = frappe.get_doc('Purchase Common')
self.check_for_closed_status(pc_obj)
self.check_for_closed_status()
frappe.db.set(self,'status','Cancelled')
@@ -197,7 +195,7 @@ class PurchaseOrder(BuyingController):
self.update_requested_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):
pass

View File

@@ -14,12 +14,14 @@ from frappe.core.doctype.communication.email import make
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.controllers.buying_controller import BuyingController
from erpnext.buying.utils import validate_for_items
STANDARD_USERS = ("Guest", "Administrator")
class RequestforQuotation(BuyingController):
def validate(self):
self.validate_duplicate_supplier()
validate_for_items(self)
self.validate_common()
self.update_email_id()
@@ -28,10 +30,6 @@ class RequestforQuotation(BuyingController):
if len(supplier_list) != len(set(supplier_list)):
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):
for rfq_supplier in self.suppliers:
if not rfq_supplier.email_id:

View File

@@ -8,6 +8,7 @@ from frappe.utils import flt
from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.buying_controller import BuyingController
from erpnext.buying.utils import validate_for_items
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@@ -24,7 +25,7 @@ class SupplierQuotation(BuyingController):
validate_status(self.status, ["Draft", "Submitted", "Stopped",
"Cancelled"])
self.validate_common()
validate_for_items(self)
self.validate_with_previous_doc()
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):
from erpnext.controllers.website_list_for_contact import get_list_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
from __future__ import unicode_literals
import frappe
import frappe, erpnext
from frappe import _, throw
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.utilities.transaction_base import TransactionBase
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
@@ -22,7 +22,7 @@ class AccountsController(TransactionBase):
@property
def company_currency(self):
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

View File

@@ -6,9 +6,10 @@ import frappe
from frappe import _, msgprint
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.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
@@ -40,9 +41,7 @@ class BuyingController(StockController):
# self.validate_purchase_return()
self.validate_rejected_warehouse()
self.validate_accepted_rejected_qty()
pc_obj = frappe.get_doc('Purchase Common')
pc_obj.validate_for_items(self)
validate_for_items(self)
#sub-contracting
self.validate_for_subcontracting()
@@ -88,9 +87,8 @@ class BuyingController(StockController):
def set_total_in_words(self):
from frappe.utils import money_in_words
company_currency = get_company_currency(self.company)
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"):
self.in_words = money_in_words(self.grand_total, self.currency)
@@ -225,9 +223,8 @@ class BuyingController(StockController):
"serial_no": rm.serial_no
})
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,
self.doctype, self.name)
self.doctype, self.name, currency=self.company_currency)
else:
rm.rate = bom_item.rate

View File

@@ -4,11 +4,9 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import cint, flt, cstr, comma_or
from erpnext.setup.utils import get_company_currency
from frappe import _, throw
from erpnext.stock.get_item_details import get_bin_details
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.controllers.stock_controller import StockController
@@ -113,13 +111,11 @@ class SellingController(StockController):
def set_total_in_words(self):
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"))
if self.meta.get_field("base_in_words"):
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"):
self.in_words = money_in_words(disable_rounded_total and
abs(self.grand_total) or abs(self.rounded_total), self.currency)

View File

@@ -97,7 +97,7 @@ class StockController(AccountsController):
def update_stock_ledger_entries(self, sle):
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_difference = flt(sle.actual_qty) * flt(sle.valuation_rate)

View File

@@ -3,10 +3,9 @@
from __future__ import unicode_literals
import json
import frappe
import frappe, erpnext
from frappe import _, scrub
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, \
validate_taxes_and_charges, validate_inclusive_tax
@@ -38,7 +37,7 @@ class calculate_taxes_and_totals(object):
def validate_conversion_rate(self):
# 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:
self.doc.currency = company_currency
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.currency, self.doc.precision("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 = \
round_based_on_smallest_currency_fraction(self.doc.base_grand_total,

View File

@@ -197,6 +197,7 @@ def make_quotation(source_name, target_doc=None):
# get default taxes
taxes = get_default_taxes_and_charges("Sales Taxes and Charges Template")
if taxes:
quotation.extend("taxes", taxes)
quotation.run_method("set_missing_values")

View File

@@ -63,6 +63,10 @@ def complete_setup(domain='Manufacturing'):
"language": "english"
})
company = erpnext.get_default_company()
company.db_set('default_payroll_payable_account',
frappe.db.get_value('Account', dict(account_name='Payroll Payable')))
def setup_demo_page():
# home page should always be "start"
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.create_salary_slips()
process_payroll.submit_salary_slips()
process_payroll.make_journal_entry(reference_date=frappe.flags.current_date,
reference_number=random_string(10))
process_payroll.make_accural_jv_entry()
# 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.create_salary_slips()
process_payroll.submit_salary_slips()
process_payroll.make_journal_entry(reference_date=frappe.flags.current_date,
reference_number=random_string(10))
process_payroll.make_accural_jv_entry()
# process_payroll.make_journal_entry(reference_date=frappe.flags.current_date,
# reference_number=random_string(10))
if frappe.db.get_global('demo_hr_user'):
make_timesheet_records()

View File

@@ -2,13 +2,12 @@
# License: GNU General Public License v3. See license.txt
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.model.naming import make_autoname
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.employee.employee import get_holiday_list_for_employee
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:
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)
if frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet"):

View File

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

View File

@@ -294,7 +294,3 @@ def get_name_with_abbr(name, company):
parts.append(company_abbr)
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.so_required = "No"
selling_settings.dn_required = "No"
selling_settings.allow_multiple_items = 1
selling_settings.save()
buying_settings = frappe.get_doc("Buying Settings")
@@ -184,6 +185,7 @@ def set_defaults(args):
buying_settings.po_required = "No"
buying_settings.pr_required = "No"
buying_settings.maintain_same_rate = 1
buying_settings.allow_multiple_items = 1
buying_settings.save()
notification_control = frappe.get_doc("Notification Control")

View File

@@ -3,19 +3,10 @@
from __future__ import unicode_literals
import frappe
from frappe import _, throw
from frappe import _
from frappe.utils import flt
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):
"""Get root element of a DocType with a tree structure"""
result = frappe.db.sql_list("""select name from `tab%s`

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.controllers.buying_controller import BuyingController
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 = {
"items": "templates/form_grid/material_request_grid.html"
@@ -72,12 +72,9 @@ class MaterialRequest(BuyingController):
from erpnext.controllers.status_updater import validate_status
validate_status(self.status, ["Draft", "Submitted", "Stopped", "Cancelled"])
pc_obj = frappe.get_doc('Purchase Common')
pc_obj.validate_for_items(self)
validate_for_items(self)
# self.set_title()
# self.validate_qty_against_so()
# 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
@@ -112,9 +109,7 @@ class MaterialRequest(BuyingController):
self.update_requested_qty()
def on_cancel(self):
pc_obj = frappe.get_doc('Purchase Common')
pc_obj.check_for_closed_status(self.doctype, self.name)
check_for_closed_status(self.doctype, self.name)
self.update_requested_qty()

View File

@@ -12,6 +12,7 @@ from frappe.utils import getdate
from erpnext.controllers.buying_controller import BuyingController
from erpnext.accounts.utils import get_account_currency
from frappe.desk.notifications import clear_doctype_notifications
from erpnext.buying.utils import check_for_closed_status, update_last_purchase_rate
form_grid_templates = {
"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("stock_uom", "stock_qty")
pc_obj = frappe.get_doc('Purchase Common')
self.check_for_closed_status(pc_obj)
self.check_for_closed_status()
if getdate(self.posting_date) > getdate(nowdate()):
throw(_("Posting Date cannot be future date"))
@@ -98,17 +98,16 @@ class PurchaseReceipt(BuyingController):
return po_qty, po_warehouse
# Check for Closed status
def check_for_closed_status(self, pc_obj):
def check_for_closed_status(self):
check_list =[]
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)
pc_obj.check_for_closed_status('Purchase Order', d.purchase_order)
check_for_closed_status('Purchase Order', d.purchase_order)
# on submit
def on_submit(self):
purchase_controller = frappe.get_doc("Purchase Common")
# Check for Approving Authority
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
self.company, self.base_grand_total)
@@ -120,7 +119,7 @@ class PurchaseReceipt(BuyingController):
self.update_billing_status()
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,
# 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]))
def on_cancel(self):
pc_obj = frappe.get_doc('Purchase Common')
self.check_for_closed_status(pc_obj)
self.check_for_closed_status()
# Check if Purchase Invoice has been submitted against current Purchase Order
submitted = frappe.db.sql("""select t1.name
from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2
@@ -157,7 +154,7 @@ class PurchaseReceipt(BuyingController):
self.update_billing_status()
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,
# 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)
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):
from erpnext.accounts.general_ledger import process_gl_map

View File

@@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
import frappe, erpnext
from frappe import _
from frappe.utils import cint, flt, cstr, now
from erpnext.stock.utils import get_valuation_method
@@ -264,7 +264,8 @@ class update_entries_after(object):
is_sample_item = self.check_if_sample_item(sle.voucher_type, sle.voucher_detail_no)
if not is_sample_item:
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):
incoming_rate = flt(sle.incoming_rate)
@@ -292,7 +293,8 @@ class update_entries_after(object):
is_sample_item = self.check_if_sample_item(sle.voucher_type, sle.voucher_detail_no)
if not is_sample_item:
_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:
_rate = 0
@@ -419,7 +421,8 @@ def get_stock_ledger_entries(previous_sle, operator=None, order="desc", limit=No
"order": order
}, 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
last_valuation_rate = frappe.db.sql("""select valuation_rate
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
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 \
and cint(frappe.db.get_value("Accounts Settings", None, "auto_accounting_for_stock")):
frappe.local.message_log = []