Merge branch 'develop'

This commit is contained in:
Rushabh Mehta 2015-11-04 18:18:08 +05:30
commit b42f3e34ef
133 changed files with 29113 additions and 22222 deletions

View File

@ -1,2 +1,2 @@
from __future__ import unicode_literals from __future__ import unicode_literals
__version__ = '6.6.7' __version__ = '6.7.0'

View File

@ -408,6 +408,8 @@ $.extend(erpnext.journal_entry, {
frappe.model.set_value(cdt, cdn, "credit", frappe.model.set_value(cdt, cdn, "credit",
flt(flt(row.credit_in_account_currency)*row.exchange_rate), precision("credit", row)); flt(flt(row.credit_in_account_currency)*row.exchange_rate), precision("credit", row));
cur_frm.cscript.update_totals(frm.doc);
}, },
set_exchange_rate: function(frm, cdt, cdn) { set_exchange_rate: function(frm, cdt, cdn) {

View File

@ -8,7 +8,7 @@ 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.setup.utils import get_company_currency
from erpnext.accounts.party import get_party_account
class JournalEntry(AccountsController): class JournalEntry(AccountsController):
def __init__(self, arg1, arg2=None): def __init__(self, arg1, arg2=None):
@ -27,6 +27,7 @@ class JournalEntry(AccountsController):
self.validate_cheque_info() self.validate_cheque_info()
self.validate_entries_for_advance() self.validate_entries_for_advance()
self.validate_multi_currency() self.validate_multi_currency()
self.set_amounts_in_company_currency()
self.validate_debit_and_credit() self.validate_debit_and_credit()
self.validate_against_jv() self.validate_against_jv()
self.validate_reference_doc() self.validate_reference_doc()
@ -175,6 +176,9 @@ class JournalEntry(AccountsController):
against_voucher = frappe.db.get_value(d.reference_type, d.reference_name, against_voucher = frappe.db.get_value(d.reference_type, d.reference_name,
[scrub(dt) for dt in field_dict.get(d.reference_type)]) [scrub(dt) for dt in field_dict.get(d.reference_type)])
if not against_voucher:
frappe.throw(_("Row {0}: Invalid reference {1}").format(d.idx, d.reference_name))
# check if party and account match # check if party and account match
if d.reference_type in ("Sales Invoice", "Purchase Invoice"): if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
if (against_voucher[0] != d.party or against_voucher[1] != d.account): if (against_voucher[0] != d.party or against_voucher[1] != d.account):
@ -279,7 +283,8 @@ class JournalEntry(AccountsController):
frappe.throw(_("Please check Multi Currency option to allow accounts with other currency")) frappe.throw(_("Please check Multi Currency option to allow accounts with other currency"))
self.set_exchange_rate() self.set_exchange_rate()
def set_amounts_in_company_currency(self):
for d in self.get("accounts"): for d in self.get("accounts"):
d.debit = flt(flt(d.debit_in_account_currency)*flt(d.exchange_rate), d.precision("debit")) d.debit = flt(flt(d.debit_in_account_currency)*flt(d.exchange_rate), d.precision("debit"))
d.credit = flt(flt(d.credit_in_account_currency)*flt(d.exchange_rate), d.precision("credit")) d.credit = flt(flt(d.credit_in_account_currency)*flt(d.exchange_rate), d.precision("credit"))
@ -498,16 +503,16 @@ def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None):
if voucher_type=="Bank Entry": if voucher_type=="Bank Entry":
account = frappe.db.get_value("Company", company, "default_bank_account") account = frappe.db.get_value("Company", company, "default_bank_account")
if not account: if not account:
account = frappe.db.get_value("Account", account = frappe.db.get_value("Account",
{"company": company, "account_type": "Bank", "is_group": 0}) {"company": company, "account_type": "Bank", "is_group": 0})
elif voucher_type=="Cash Entry": elif voucher_type=="Cash Entry":
account = frappe.db.get_value("Company", company, "default_cash_account") account = frappe.db.get_value("Company", company, "default_cash_account")
if not account: if not account:
account = frappe.db.get_value("Account", account = frappe.db.get_value("Account",
{"company": company, "account_type": "Cash", "is_group": 0}) {"company": company, "account_type": "Cash", "is_group": 0})
if account: if account:
account_details = frappe.db.get_value("Account", account, account_details = frappe.db.get_value("Account", account,
["account_currency", "account_type"], as_dict=1) ["account_currency", "account_type"], as_dict=1)
return { return {
"account": account, "account": account,
@ -515,211 +520,118 @@ def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None):
"account_currency": account_details.account_currency, "account_currency": account_details.account_currency,
"account_type": account_details.account_type "account_type": account_details.account_type
} }
@frappe.whitelist() @frappe.whitelist()
def get_payment_entry_from_sales_invoice(sales_invoice): def get_payment_entry_against_order(dt, dn):
"""Returns new Journal Entry document as dict for given Sales Invoice""" ref_doc = frappe.get_doc(dt, dn)
from erpnext.accounts.utils import get_balance_on
si = frappe.get_doc("Sales Invoice", sales_invoice) if flt(ref_doc.per_billed, 2) > 0:
frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
# exchange rate
exchange_rate = get_exchange_rate(si.debit_to, si.party_account_currency, si.company, if dt == "Sales Order":
si.doctype, si.name) party_type = "Customer"
amount_field_party = "credit_in_account_currency"
jv = get_payment_entry(si) amount_field_bank = "debit_in_account_currency"
jv.remark = 'Payment received against Sales Invoice {0}. {1}'.format(si.name, si.remarks)
# credit customer
row1 = jv.get("accounts")[0]
row1.account = si.debit_to
row1.account_currency = si.party_account_currency
row1.party_type = "Customer"
row1.party = si.customer
row1.balance = get_balance_on(si.debit_to)
row1.party_balance = get_balance_on(party=si.customer, party_type="Customer")
row1.credit_in_account_currency = si.outstanding_amount
row1.reference_type = si.doctype
row1.reference_name = si.name
row1.exchange_rate = exchange_rate
row1.account_type = "Receivable" if si.customer else ""
# debit bank
row2 = jv.get("accounts")[1]
if row2.account_currency == si.party_account_currency:
row2.debit_in_account_currency = si.outstanding_amount
else: else:
row2.debit_in_account_currency = si.outstanding_amount * exchange_rate party_type = "Supplier"
amount_field_party = "debit_in_account_currency"
# set multi currency check amount_field_bank = "credit_in_account_currency"
if row1.account_currency != si.company_currency or row2.account_currency != si.company_currency:
jv.multi_currency = 1 party_account = get_party_account(party_type, ref_doc.get(party_type.lower()), ref_doc.company)
return jv.as_dict()
@frappe.whitelist()
def get_payment_entry_from_purchase_invoice(purchase_invoice):
"""Returns new Journal Entry document as dict for given Purchase Invoice"""
pi = frappe.get_doc("Purchase Invoice", purchase_invoice)
exchange_rate = get_exchange_rate(pi.credit_to, pi.party_account_currency, pi.company,
pi.doctype, pi.name)
jv = get_payment_entry(pi)
jv.remark = 'Payment against Purchase Invoice {0}. {1}'.format(pi.name, pi.remarks)
jv.exchange_rate = exchange_rate
# credit supplier
row1 = jv.get("accounts")[0]
row1.account = pi.credit_to
row1.account_currency = pi.party_account_currency
row1.party_type = "Supplier"
row1.party = pi.supplier
row1.balance = get_balance_on(pi.credit_to)
row1.party_balance = get_balance_on(party=pi.supplier, party_type="Supplier")
row1.debit_in_account_currency = pi.outstanding_amount
row1.reference_type = pi.doctype
row1.reference_name = pi.name
row1.exchange_rate = exchange_rate
row1.account_type = "Payable" if pi.supplier else ""
# credit bank
row2 = jv.get("accounts")[1]
if row2.account_currency == pi.party_account_currency:
row2.credit_in_account_currency = pi.outstanding_amount
else:
row2.credit_in_account_currency = pi.outstanding_amount * exchange_rate
# set multi currency check
if row1.account_currency != pi.company_currency or row2.account_currency != pi.company_currency:
jv.multi_currency = 1
return jv.as_dict()
@frappe.whitelist()
def get_payment_entry_from_sales_order(sales_order):
"""Returns new Journal Entry document as dict for given Sales Order"""
from erpnext.accounts.utils import get_balance_on
from erpnext.accounts.party import get_party_account
so = frappe.get_doc("Sales Order", sales_order)
if flt(so.per_billed, 2) != 0.0:
frappe.throw(_("Can only make payment against unbilled Sales Order"))
jv = get_payment_entry(so)
jv.remark = 'Advance payment received against Sales Order {0}.'.format(so.name)
party_account = get_party_account("Customer", so.customer, so.company)
party_account_currency = get_account_currency(party_account) party_account_currency = get_account_currency(party_account)
exchange_rate = get_exchange_rate(party_account, party_account_currency, so.company) if party_account_currency == ref_doc.company_currency:
amount = flt(ref_doc.base_grand_total) - flt(ref_doc.advance_paid)
if party_account_currency == so.company_currency:
amount = flt(so.base_grand_total) - flt(so.advance_paid)
else: else:
amount = flt(so.grand_total) - flt(so.advance_paid) amount = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid)
# credit customer return get_payment_entry(ref_doc, {
row1 = jv.get("accounts")[0] "party_type": party_type,
row1.account = party_account "party_account": party_account,
row1.account_currency = party_account_currency "party_account_currency": party_account_currency,
row1.party_type = "Customer" "amount_field_party": amount_field_party,
row1.party = so.customer "amount_field_bank": amount_field_bank,
row1.balance = get_balance_on(party_account) "amount": amount,
row1.party_balance = get_balance_on(party=so.customer, party_type="Customer") "remarks": 'Advance Payment received against {0} {1}'.format(dt, dn),
row1.credit_in_account_currency = amount "is_advance": "Yes"
row1.reference_type = so.doctype })
row1.reference_name = so.name
row1.is_advance = "Yes"
row1.exchange_rate = exchange_rate
row1.account_type = "Receivable"
# debit bank
row2 = jv.get("accounts")[1]
if row2.account_currency == party_account_currency:
row2.debit_in_account_currency = amount
else:
row2.debit_in_account_currency = amount * exchange_rate
# set multi currency check
if row1.account_currency != so.company_currency or row2.account_currency != so.company_currency:
jv.multi_currency = 1
return jv.as_dict()
@frappe.whitelist() @frappe.whitelist()
def get_payment_entry_from_purchase_order(purchase_order): def get_payment_entry_against_invoice(dt, dn):
"""Returns new Journal Entry document as dict for given Sales Order""" ref_doc = frappe.get_doc(dt, dn)
from erpnext.accounts.utils import get_balance_on if dt == "Sales Invoice":
from erpnext.accounts.party import get_party_account party_type = "Customer"
po = frappe.get_doc("Purchase Order", purchase_order) party_account = ref_doc.debit_to
amount_field_party = "credit_in_account_currency"
if flt(po.per_billed, 2) != 0.0: amount_field_bank = "debit_in_account_currency"
frappe.throw(_("Can only make payment against unbilled Sales Order"))
jv = get_payment_entry(po)
jv.remark = 'Advance payment made against Purchase Order {0}.'.format(po.name)
party_account = get_party_account("Supplier", po.supplier, po.company)
party_account_currency = get_account_currency(party_account)
exchange_rate = get_exchange_rate(party_account, party_account_currency, po.company)
if party_account_currency == po.company_currency:
amount = flt(po.base_grand_total) - flt(po.advance_paid)
else: else:
amount = flt(po.grand_total) - flt(po.advance_paid) party_type = "Supplier"
party_account = ref_doc.credit_to
amount_field_party = "debit_in_account_currency"
amount_field_bank = "credit_in_account_currency"
return get_payment_entry(ref_doc, {
"party_type": party_type,
"party_account": party_account,
"party_account_currency": ref_doc.party_account_currency,
"amount_field_party": amount_field_party,
"amount_field_bank": amount_field_bank,
"amount": ref_doc.outstanding_amount,
"remarks": 'Payment received against {0} {1}. {2}'.format(dt, dn, ref_doc.remarks),
"is_advance": "No"
})
def get_payment_entry(ref_doc, args):
cost_center = frappe.db.get_value("Company", ref_doc.company, "cost_center")
exchange_rate = get_exchange_rate(args.get("party_account"), args.get("party_account_currency"),
ref_doc.company, ref_doc.doctype, ref_doc.name)
# credit customer jv = frappe.new_doc("Journal Entry")
row1 = jv.get("accounts")[0] jv.update({
row1.account = party_account "voucher_type": "Bank Entry",
row1.party_type = "Supplier" "company": ref_doc.company,
row1.party = po.supplier "remark": args.get("remarks")
row1.balance = get_balance_on(party_account) })
row1.party_balance = get_balance_on(party=po.supplier, party_type="Supplier")
row1.debit_in_account_currency = amount party_row = jv.append("accounts", {
row1.reference_type = po.doctype "account": args.get("party_account"),
row1.reference_name = po.name "party_type": args.get("party_type"),
row1.is_advance = "Yes" "party": ref_doc.get(args.get("party_type").lower()),
row1.exchange_rate = exchange_rate "cost_center": cost_center,
row1.account_type = "Payable" "account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"),
"account_currency": args.get("party_account_currency") or \
# debit bank get_account_currency(args.get("party_account")),
row2 = jv.get("accounts")[1] "account_balance": get_balance_on(args.get("party_account")),
if row2.account_currency == party_account_currency: "party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")),
row2.credit_in_account_currency = amount "exchange_rate": exchange_rate,
else: args.get("amount_field_party"): args.get("amount"),
row2.credit_in_account_currency = amount * exchange_rate "is_advance": args.get("is_advance"),
"reference_type": ref_doc.doctype,
# set multi currency check "reference_name": ref_doc.name
if row1.account_currency != po.company_currency or row2.account_currency != po.company_currency: })
jv.multi_currency = 1
return jv.as_dict()
def get_payment_entry(doc):
bank_account = get_default_bank_cash_account(doc.company, "Bank Entry")
cost_center = frappe.db.get_value("Company", doc.company, "cost_center")
jv = frappe.new_doc('Journal Entry')
jv.voucher_type = 'Bank Entry'
jv.company = doc.company
jv.fiscal_year = doc.fiscal_year
d1 = jv.append("accounts")
d1.cost_center = cost_center
d2 = jv.append("accounts")
bank_row = jv.append("accounts")
bank_account = get_default_bank_cash_account(ref_doc.company, "Bank Entry")
if bank_account: if bank_account:
d2.account = bank_account["account"] bank_row.update(bank_account)
d2.balance = bank_account["balance"] bank_row.exchange_rate = get_exchange_rate(bank_account["account"],
d2.account_currency = bank_account["account_currency"] bank_account["account_currency"], ref_doc.company)
d2.account_type = bank_account["account_type"]
d2.exchange_rate = get_exchange_rate(bank_account["account"], bank_row.cost_center = cost_center
bank_account["account_currency"], doc.company)
d2.cost_center = cost_center if bank_row.account_currency == args.get("party_account_currency"):
bank_row.set(args.get("amount_field_bank"), args.get("amount"))
else:
bank_row.set(args.get("amount_field_bank"), args.get("amount") * exchange_rate)
return jv # set multi currency check
if party_row.account_currency != ref_doc.company_currency \
or (bank_row.account_currency and bank_row.account_currency != ref_doc.company_currency):
jv.multi_currency = 1
jv.set_amounts_in_company_currency()
return jv.as_dict()
@frappe.whitelist() @frappe.whitelist()
def get_opening_accounts(company): def get_opening_accounts(company):
@ -781,7 +693,6 @@ def get_party_account_and_balance(company, party_type, party):
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)
from erpnext.accounts.party import get_party_account
account = get_party_account(party_type, party, company) account = get_party_account(party_type, party, company)
account_balance = get_balance_on(account=account) account_balance = get_balance_on(account=account)
@ -823,15 +734,15 @@ def get_account_balance_and_party_type(account, date, company, debit=None, credi
def get_exchange_rate(account, account_currency=None, company=None, def get_exchange_rate(account, account_currency=None, company=None,
reference_type=None, reference_name=None, debit=None, credit=None, exchange_rate=None): reference_type=None, reference_name=None, debit=None, credit=None, exchange_rate=None):
from erpnext.setup.utils import get_exchange_rate from erpnext.setup.utils import get_exchange_rate
account_details = frappe.db.get_value("Account", account, account_details = frappe.db.get_value("Account", account,
["account_type", "root_type", "account_currency", "company"], as_dict=1) ["account_type", "root_type", "account_currency", "company"], as_dict=1)
if not company: if not company:
company = account_details.company company = account_details.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 = get_company_currency(company)
if account_currency != company_currency: if account_currency != company_currency:

View File

@ -38,7 +38,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
get_query_filters: { get_query_filters: {
supplier: cur_frm.doc.supplier || undefined, supplier: cur_frm.doc.supplier || undefined,
docstatus: 1, docstatus: 1,
status: ["!=", "Stopped"], status: ["not in", ["Stopped", "Closed"]],
per_billed: ["<", 99.99], per_billed: ["<", 99.99],
company: cur_frm.doc.company company: cur_frm.doc.company
} }
@ -52,6 +52,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
get_query_filters: { get_query_filters: {
supplier: cur_frm.doc.supplier || undefined, supplier: cur_frm.doc.supplier || undefined,
docstatus: 1, docstatus: 1,
status: ["!=", "Closed"],
company: cur_frm.doc.company company: cur_frm.doc.company
} }
}) })
@ -135,9 +136,10 @@ cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);
cur_frm.cscript.make_bank_entry = function() { cur_frm.cscript.make_bank_entry = function() {
return frappe.call({ return frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_from_purchase_invoice", method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_invoice",
args: { args: {
"purchase_invoice": cur_frm.doc.name, "dt": "Purchase Invoice",
"dn": cur_frm.doc.name
}, },
callback: function(r) { callback: function(r) {
var doclist = frappe.model.sync(r.message); var doclist = frappe.model.sync(r.message);

View File

@ -48,7 +48,7 @@ class PurchaseInvoice(BuyingController):
self.check_conversion_rate() self.check_conversion_rate()
self.validate_credit_to_acc() self.validate_credit_to_acc()
self.clear_unallocated_advances("Purchase Invoice Advance", "advances") self.clear_unallocated_advances("Purchase Invoice Advance", "advances")
self.check_for_stopped_status() self.check_for_stopped_or_closed_status()
self.validate_with_previous_doc() self.validate_with_previous_doc()
self.validate_uom_is_integer("uom", "qty") self.validate_uom_is_integer("uom", "qty")
self.set_against_expense_account() self.set_against_expense_account()
@ -103,14 +103,14 @@ class PurchaseInvoice(BuyingController):
self.party_account_currency = account.account_currency self.party_account_currency = account.account_currency
def check_for_stopped_status(self): def check_for_stopped_or_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)
stopped = frappe.db.sql("select name from `tabPurchase Order` where status = 'Stopped' and name = %s", d.purchase_order) pc_obj.check_for_stopped_or_closed_status('Purchase Order', d.purchase_order)
if stopped:
throw(_("Purchase Order {0} is 'Stopped'").format(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({
@ -394,6 +394,8 @@ class PurchaseInvoice(BuyingController):
make_gl_entries(gl_entries, cancel=(self.docstatus == 2)) make_gl_entries(gl_entries, cancel=(self.docstatus == 2))
def on_cancel(self): def on_cancel(self):
self.check_for_stopped_or_closed_status()
if not self.is_return: if not self.is_return:
from erpnext.accounts.utils import remove_against_link_from_jv from erpnext.accounts.utils import remove_against_link_from_jv
remove_against_link_from_jv(self.doctype, self.name) remove_against_link_from_jv(self.doctype, self.name)

View File

@ -50,6 +50,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
if(doc.update_stock) this.show_stock_ledger(); if(doc.update_stock) this.show_stock_ledger();
if(doc.docstatus==1 && !doc.is_return) { if(doc.docstatus==1 && !doc.is_return) {
var is_delivered_by_supplier = false;
is_delivered_by_supplier = cur_frm.doc.items.some(function(item){
return item.is_delivered_by_supplier ? true : false;
})
cur_frm.add_custom_button(doc.update_stock ? __('Sales Return') : __('Credit Note'), cur_frm.add_custom_button(doc.update_stock ? __('Sales Return') : __('Credit Note'),
this.make_sales_return); this.make_sales_return);
@ -61,7 +68,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
return item.delivery_note ? true : false; return item.delivery_note ? true : false;
}); });
if(!from_delivery_note) { if(!from_delivery_note && !is_delivered_by_supplier) {
cur_frm.add_custom_button(__('Delivery'), cur_frm.cscript['Make Delivery Note']).addClass("btn-primary"); cur_frm.add_custom_button(__('Delivery'), cur_frm.cscript['Make Delivery Note']).addClass("btn-primary");
} }
} }
@ -104,7 +111,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
source_doctype: "Sales Order", source_doctype: "Sales Order",
get_query_filters: { get_query_filters: {
docstatus: 1, docstatus: 1,
status: ["!=", "Stopped"], status: ["not in", ["Stopped", "Closed"]],
per_billed: ["<", 99.99], per_billed: ["<", 99.99],
customer: cur_frm.doc.customer || undefined, customer: cur_frm.doc.customer || undefined,
company: cur_frm.doc.company company: cur_frm.doc.company
@ -323,9 +330,10 @@ cur_frm.cscript['Make Delivery Note'] = function() {
cur_frm.cscript.make_bank_entry = function() { cur_frm.cscript.make_bank_entry = function() {
return frappe.call({ return frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_from_sales_invoice", method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_invoice",
args: { args: {
"sales_invoice": cur_frm.doc.name "dt": "Sales Invoice",
"dn": cur_frm.doc.name
}, },
callback: function(r) { callback: function(r) {
var doclist = frappe.model.sync(r.message); var doclist = frappe.model.sync(r.message);

View File

@ -65,7 +65,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Series", "label": "Series",
"no_copy": 1, "no_copy": 1,
"oldfieldname": "naming_series", "oldfieldname": "naming_series",
"oldfieldtype": "Select", "oldfieldtype": "Select",
@ -1168,7 +1168,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Apply Additional Discount On", "label": "Apply Additional Discount On",
"no_copy": 0, "no_copy": 0,
"options": "\nGrand Total\nNet Total", "options": "\nGrand Total\nNet Total",
"permlevel": 0, "permlevel": 0,
@ -2211,7 +2211,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Source", "label": "Source",
"no_copy": 0, "no_copy": 0,
"oldfieldname": "source", "oldfieldname": "source",
"oldfieldtype": "Select", "oldfieldtype": "Select",
@ -2308,7 +2308,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 1, "in_filter": 1,
"in_list_view": 0, "in_list_view": 0,
"label": "Is Opening Entry", "label": "Is Opening Entry",
"no_copy": 0, "no_copy": 0,
"oldfieldname": "is_opening", "oldfieldname": "is_opening",
"oldfieldtype": "Select", "oldfieldtype": "Select",
@ -2332,7 +2332,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "C-Form Applicable", "label": "C-Form Applicable",
"no_copy": 1, "no_copy": 1,
"options": "No\nYes", "options": "No\nYes",
"permlevel": 0, "permlevel": 0,
@ -2700,7 +2700,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Recurring Type", "label": "Recurring Type",
"no_copy": 1, "no_copy": 1,
"options": "\nMonthly\nQuarterly\nHalf-yearly\nYearly", "options": "\nMonthly\nQuarterly\nHalf-yearly\nYearly",
"permlevel": 0, "permlevel": 0,
@ -2951,7 +2951,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-10-02 07:39:09.123982", "modified": "2015-10-26 12:12:40.616546",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice", "name": "Sales Invoice",

View File

@ -52,7 +52,7 @@ class SalesInvoice(SellingController):
self.validate_proj_cust() self.validate_proj_cust()
self.validate_with_previous_doc() self.validate_with_previous_doc()
self.validate_uom_is_integer("stock_uom", "qty") self.validate_uom_is_integer("stock_uom", "qty")
self.check_stop_sales_order("sales_order") self.check_stop_or_close_sales_order("sales_order")
self.validate_debit_to_acc() self.validate_debit_to_acc()
self.validate_fixed_asset_account() self.validate_fixed_asset_account()
self.clear_unallocated_advances("Sales Invoice Advance", "advances") self.clear_unallocated_advances("Sales Invoice Advance", "advances")
@ -117,7 +117,7 @@ class SalesInvoice(SellingController):
if cint(self.update_stock) == 1: if cint(self.update_stock) == 1:
self.update_stock_ledger() self.update_stock_ledger()
self.check_stop_sales_order("sales_order") self.check_stop_or_close_sales_order("sales_order")
from erpnext.accounts.utils import remove_against_link_from_jv from erpnext.accounts.utils import remove_against_link_from_jv
remove_against_link_from_jv(self.doctype, self.name) remove_against_link_from_jv(self.doctype, self.name)
@ -667,7 +667,8 @@ def make_delivery_note(source_name, target_doc=None):
"sales_order": "against_sales_order", "sales_order": "against_sales_order",
"so_detail": "so_detail" "so_detail": "so_detail"
}, },
"postprocess": update_item "postprocess": update_item,
"condition": lambda doc: doc.delivered_by_supplier!=1
}, },
"Sales Taxes and Charges": { "Sales Taxes and Charges": {
"doctype": "Sales Taxes and Charges", "doctype": "Sales Taxes and Charges",

View File

@ -680,6 +680,51 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "eval:doc.delivered_by_supplier==1",
"fieldname": "drop_ship",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Drop Ship",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "delivered_by_supplier",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Delivered By Supplier",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1,
"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,
@ -1282,7 +1327,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2015-10-19 03:04:52.093181", "modified": "2015-11-02 15:14:02.306067",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice Item", "name": "Sales Invoice Item",

View File

@ -1,16 +1,18 @@
{ {
"add_total_row": 0,
"apply_user_permissions": 1, "apply_user_permissions": 1,
"creation": "2013-07-30 17:28:49", "creation": "2013-07-30 17:28:49",
"disabled": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Report", "doctype": "Report",
"idx": 1, "idx": 1,
"is_standard": "Yes", "is_standard": "Yes",
"modified": "2015-03-30 05:33:45.353064", "modified": "2015-11-02 12:32:02.048551",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Delivered Items To Be Billed", "name": "Delivered Items To Be Billed",
"owner": "Administrator", "owner": "Administrator",
"query": "select\n `tabDelivery Note`.`name` as \"Delivery Note:Link/Delivery Note:120\",\n\t`tabDelivery Note`.`customer` as \"Customer:Link/Customer:120\",\n\t`tabDelivery Note`.`posting_date` as \"Date:Date\",\n\t`tabDelivery Note`.`project_name` as \"Project\",\n\t`tabDelivery Note Item`.`item_code` as \"Item:Link/Item:120\",\n\t(`tabDelivery Note Item`.`qty` - ifnull((select sum(qty) from `tabSales Invoice Item` \n\t where `tabSales Invoice Item`.docstatus=1 and \n `tabSales Invoice Item`.delivery_note = `tabDelivery Note`.name and\n\t `tabSales Invoice Item`.dn_detail = `tabDelivery Note Item`.name), 0))\n\t\tas \"Qty:Float:110\",\n\t(`tabDelivery Note Item`.`base_amount` - ifnull((select sum(base_amount) from `tabSales Invoice Item` \n where `tabSales Invoice Item`.docstatus=1 and \n `tabSales Invoice Item`.delivery_note = `tabDelivery Note`.name and\n `tabSales Invoice Item`.dn_detail = `tabDelivery Note Item`.name), 0))\n\t\tas \"Amount:Currency:110\",\n\t`tabDelivery Note Item`.`item_name` as \"Item Name::150\",\n\t`tabDelivery Note Item`.`description` as \"Description::200\",\n\t`tabDelivery Note`.`company` as \"Company:Link/Company:\"\nfrom `tabDelivery Note`, `tabDelivery Note Item`\nwhere\n `tabDelivery Note`.docstatus = 1 and\n\t`tabDelivery Note`.`status` != \"Stopped\" and\n `tabDelivery Note`.name = `tabDelivery Note Item`.parent and\n (`tabDelivery Note Item`.qty > ifnull((select sum(qty) from `tabSales Invoice Item` \n where `tabSales Invoice Item`.docstatus=1 and \n `tabSales Invoice Item`.delivery_note = `tabDelivery Note`.name and\n `tabSales Invoice Item`.dn_detail = `tabDelivery Note Item`.name), 0))\norder by `tabDelivery Note`.`name` desc", "query": "select\n `tabDelivery Note`.`name` as \"Delivery Note:Link/Delivery Note:120\",\n\t`tabDelivery Note`.`customer` as \"Customer:Link/Customer:120\",\n\t`tabDelivery Note`.`posting_date` as \"Date:Date\",\n\t`tabDelivery Note`.`project_name` as \"Project\",\n\t`tabDelivery Note Item`.`item_code` as \"Item:Link/Item:120\",\n\t(`tabDelivery Note Item`.`qty` - ifnull((select sum(qty) from `tabSales Invoice Item` \n\t where `tabSales Invoice Item`.docstatus=1 and \n `tabSales Invoice Item`.delivery_note = `tabDelivery Note`.name and\n\t `tabSales Invoice Item`.dn_detail = `tabDelivery Note Item`.name), 0))\n\t\tas \"Qty:Float:110\",\n\t(`tabDelivery Note Item`.`base_amount` - ifnull((select sum(base_amount) from `tabSales Invoice Item` \n where `tabSales Invoice Item`.docstatus=1 and \n `tabSales Invoice Item`.delivery_note = `tabDelivery Note`.name and\n `tabSales Invoice Item`.dn_detail = `tabDelivery Note Item`.name), 0))\n\t\tas \"Amount:Currency:110\",\n\t`tabDelivery Note Item`.`item_name` as \"Item Name::150\",\n\t`tabDelivery Note Item`.`description` as \"Description::200\",\n\t`tabDelivery Note`.`company` as \"Company:Link/Company:\"\nfrom `tabDelivery Note`, `tabDelivery Note Item`\nwhere\n `tabDelivery Note`.docstatus = 1 and\n\t`tabDelivery Note`.`status` not in (\"Stopped\", \"Closed\") and\n `tabDelivery Note`.name = `tabDelivery Note Item`.parent and\n (`tabDelivery Note Item`.qty > ifnull((select sum(qty) from `tabSales Invoice Item` \n where `tabSales Invoice Item`.docstatus=1 and \n `tabSales Invoice Item`.delivery_note = `tabDelivery Note`.name and\n `tabSales Invoice Item`.dn_detail = `tabDelivery Note Item`.name), 0))\norder by `tabDelivery Note`.`name` desc",
"ref_doctype": "Sales Invoice", "ref_doctype": "Sales Invoice",
"report_name": "Delivered Items To Be Billed", "report_name": "Delivered Items To Be Billed",
"report_type": "Query Report" "report_type": "Query Report"

View File

@ -1,16 +1,18 @@
{ {
"add_total_row": 0,
"apply_user_permissions": 1, "apply_user_permissions": 1,
"creation": "2013-02-21 14:26:44", "creation": "2013-02-21 14:26:44",
"disabled": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Report", "doctype": "Report",
"idx": 1, "idx": 1,
"is_standard": "Yes", "is_standard": "Yes",
"modified": "2015-03-30 05:33:29.382709", "modified": "2015-11-04 11:56:32.699103",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Ordered Items To Be Billed", "name": "Ordered Items To Be Billed",
"owner": "Administrator", "owner": "Administrator",
"query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`status` as \"Status\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project_name` as \"Project\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.base_amount as \"Amount:Currency:110\",\n (`tabSales Order Item`.billed_amt * ifnull(`tabSales Order`.conversion_rate, 1)) as \"Billed Amount:Currency:110\",\n (ifnull(`tabSales Order Item`.base_amount, 0) - (ifnull(`tabSales Order Item`.billed_amt, 0) * ifnull(`tabSales Order`.conversion_rate, 1))) as \"Pending Amount:Currency:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order`.`company` as \"Company:Link/Company:\"\nfrom\n `tabSales Order`, `tabSales Order Item`\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status != \"Stopped\"\n and ifnull(`tabSales Order Item`.billed_amt,0) < ifnull(`tabSales Order Item`.amount,0)\norder by `tabSales Order`.transaction_date asc", "query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`status` as \"Status\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project_name` as \"Project\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.base_amount as \"Amount:Currency:110\",\n (`tabSales Order Item`.billed_amt * ifnull(`tabSales Order`.conversion_rate, 1)) as \"Billed Amount:Currency:110\",\n (ifnull(`tabSales Order Item`.base_amount, 0) - (ifnull(`tabSales Order Item`.billed_amt, 0) * ifnull(`tabSales Order`.conversion_rate, 1))) as \"Pending Amount:Currency:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order`.`company` as \"Company:Link/Company:\"\nfrom\n `tabSales Order`, `tabSales Order Item`\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status not in (\"Stopped\", \"Closed\")\n and ifnull(`tabSales Order Item`.billed_amt,0) < ifnull(`tabSales Order Item`.amount,0)\norder by `tabSales Order`.transaction_date asc",
"ref_doctype": "Sales Invoice", "ref_doctype": "Sales Invoice",
"report_name": "Ordered Items To Be Billed", "report_name": "Ordered Items To Be Billed",
"report_type": "Query Report" "report_type": "Query Report"

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from erpnext.accounts.report.accounts_receivable.accounts_receivable import get_ageing_data from erpnext.accounts.report.accounts_receivable.accounts_receivable import get_ageing_data
from frappe.utils import flt from frappe.utils import flt, getdate
def execute(filters=None): def execute(filters=None):
if not filters: filters = {} if not filters: filters = {}
@ -13,25 +13,27 @@ def execute(filters=None):
columns = get_columns(filters) columns = get_columns(filters)
entries = get_entries(filters) entries = get_entries(filters)
invoice_posting_date_map = get_invoice_posting_date_map(filters) invoice_details = get_invoice_posting_date_map(filters)
against_date = "" against_date = ""
data = [] data = []
for d in entries: for d in entries:
against_date = invoice_posting_date_map.get(d.reference_name) or "" invoice = invoice_details.get(d.reference_name) or frappe._dict()
if d.reference_type=="Purchase Invoice": if d.reference_type=="Purchase Invoice":
payment_amount = flt(d.debit) or -1 * flt(d.credit) payment_amount = flt(d.debit) or -1 * flt(d.credit)
else: else:
payment_amount = flt(d.credit) or -1 * flt(d.debit) payment_amount = flt(d.credit) or -1 * flt(d.debit)
row = [d.name, d.party_type, d.party, d.posting_date, d.reference_name, row = [d.name, d.party_type, d.party, d.posting_date, d.reference_name, invoice.posting_date,
against_date, d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark] invoice.due_date, d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark]
if d.reference_name: if d.reference_name:
row += get_ageing_data(30, 60, 90, d.posting_date, against_date, payment_amount) row += get_ageing_data(30, 60, 90, d.posting_date, against_date, payment_amount)
else: else:
row += ["", "", "", "", ""] row += ["", "", "", "", ""]
if invoice.due_date:
row.append((getdate(d.posting_date) - getdate(invoice.due_date)).days or 0)
data.append(row) data.append(row)
return columns, data return columns, data
@ -43,13 +45,25 @@ def validate_filters(filters):
.format(filters.payment_type, filters.party_type)) .format(filters.payment_type, filters.party_type))
def get_columns(filters): def get_columns(filters):
return [_("Journal Entry") + ":Link/Journal Entry:140", return [
_("Party Type") + "::100", _("Party") + ":Dynamic Link/Party Type:140", _("Journal Entry") + ":Link/Journal Entry:140",
_("Party Type") + "::100",
_("Party") + ":Dynamic Link/Party Type:140",
_("Posting Date") + ":Date:100", _("Posting Date") + ":Date:100",
_("Against Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == "Outgoing" else ":Link/Sales Invoice:130"), _("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == "Outgoing" else ":Link/Sales Invoice:130"),
_("Against Invoice Posting Date") + ":Date:130", _("Debit") + ":Currency:120", _("Credit") + ":Currency:120", _("Invoice Posting Date") + ":Date:130",
_("Reference No") + "::100", _("Reference Date") + ":Date:100", _("Remarks") + "::150", _("Age") +":Int:40", _("Payment Due Date") + ":Date:130",
"0-30:Currency:100", "30-60:Currency:100", "60-90:Currency:100", _("90-Above") + ":Currency:100" _("Debit") + ":Currency:120",
_("Credit") + ":Currency:120",
_("Reference No") + "::100",
_("Reference Date") + ":Date:100",
_("Remarks") + "::150",
_("Age") +":Int:40",
"0-30:Currency:100",
"30-60:Currency:100",
"60-90:Currency:100",
_("90-Above") + ":Currency:100",
_("Delay in payment (Days)") + "::150"
] ]
def get_conditions(filters): def get_conditions(filters):
@ -66,7 +80,14 @@ def get_conditions(filters):
if filters.get("party"): if filters.get("party"):
conditions.append("jvd.party=%(party)s") conditions.append("jvd.party=%(party)s")
if filters.get("party_type"):
conditions.append("jvd.reference_type=%(reference_type)s")
if filters.get("party_type") == "Customer":
filters["reference_type"] = "Sales Invoice"
else:
filters["reference_type"] = "Purchase Invoice"
if filters.get("company"): if filters.get("company"):
conditions.append("jv.company=%(company)s") conditions.append("jv.company=%(company)s")
@ -89,12 +110,9 @@ def get_entries(filters):
return entries return entries
def get_invoice_posting_date_map(filters): def get_invoice_posting_date_map(filters):
invoice_posting_date_map = {} invoice_details = {}
if filters.get("payment_type") == "Incoming": dt = "Sales Invoice" if filters.get("payment_type") == "Incoming" else "Purchase Invoice"
for t in frappe.db.sql("""select name, posting_date from `tabSales Invoice`"""): for t in frappe.db.sql("select name, posting_date, due_date from `tab{0}`".format(dt), as_dict=1):
invoice_posting_date_map[t[0]] = t[1] invoice_details[t.name] = t
else:
for t in frappe.db.sql("""select name, posting_date from `tabPurchase Invoice`"""):
invoice_posting_date_map[t[0]] = t[1]
return invoice_posting_date_map return invoice_details

View File

@ -2,16 +2,17 @@
"add_total_row": 1, "add_total_row": 1,
"apply_user_permissions": 1, "apply_user_permissions": 1,
"creation": "2013-05-28 15:54:16", "creation": "2013-05-28 15:54:16",
"disabled": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Report", "doctype": "Report",
"idx": 1, "idx": 1,
"is_standard": "Yes", "is_standard": "Yes",
"modified": "2015-03-30 05:37:23.626083", "modified": "2015-11-04 11:56:14.321664",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Order Items To Be Billed", "name": "Purchase Order Items To Be Billed",
"owner": "Administrator", "owner": "Administrator",
"query": "select \n `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n `tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order Item`.`project_name` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.base_amount as \"Amount:Currency:100\",\n\t(`tabPurchase Order Item`.billed_amt * ifnull(`tabPurchase Order`.conversion_rate, 1)) as \"Billed Amount:Currency:100\", \n\t(`tabPurchase Order Item`.base_amount - (ifnull(`tabPurchase Order Item`.billed_amt, 0) * ifnull(`tabPurchase Order`.conversion_rate, 1))) as \"Amount to Bill:Currency:100\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\",\n\t`tabPurchase Order`.company as \"Company:Link/Company:\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status != \"Stopped\"\n\tand (ifnull(`tabPurchase Order Item`.billed_amt, 0) * ifnull(`tabPurchase Order`.conversion_rate, 1)) < ifnull(`tabPurchase Order Item`.base_amount, 0)\norder by `tabPurchase Order`.transaction_date asc", "query": "select \n `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n `tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order Item`.`project_name` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.base_amount as \"Amount:Currency:100\",\n\t(`tabPurchase Order Item`.billed_amt * ifnull(`tabPurchase Order`.conversion_rate, 1)) as \"Billed Amount:Currency:100\", \n\t(`tabPurchase Order Item`.base_amount - (ifnull(`tabPurchase Order Item`.billed_amt, 0) * ifnull(`tabPurchase Order`.conversion_rate, 1))) as \"Amount to Bill:Currency:100\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\",\n\t`tabPurchase Order`.company as \"Company:Link/Company:\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status not in (\"Stopped\", \"Closed\")\n\tand (ifnull(`tabPurchase Order Item`.billed_amt, 0) * ifnull(`tabPurchase Order`.conversion_rate, 1)) < ifnull(`tabPurchase Order Item`.base_amount, 0)\norder by `tabPurchase Order`.transaction_date asc",
"ref_doctype": "Purchase Invoice", "ref_doctype": "Purchase Invoice",
"report_name": "Purchase Order Items To Be Billed", "report_name": "Purchase Order Items To Be Billed",
"report_type": "Query Report" "report_type": "Query Report"

View File

@ -1,16 +1,18 @@
{ {
"add_total_row": 0,
"apply_user_permissions": 1, "apply_user_permissions": 1,
"creation": "2013-07-30 18:35:10", "creation": "2013-07-30 18:35:10",
"disabled": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Report", "doctype": "Report",
"idx": 1, "idx": 1,
"is_standard": "Yes", "is_standard": "Yes",
"modified": "2015-04-14 11:56:02.323769", "modified": "2015-11-02 12:33:11.681513",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Received Items To Be Billed", "name": "Received Items To Be Billed",
"owner": "Administrator", "owner": "Administrator",
"query": "select\n `tabPurchase Receipt`.`name` as \"Purchase Receipt:Link/Purchase Receipt:120\",\n `tabPurchase Receipt`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Receipt`.`posting_date` as \"Date:Date\",\n\t`tabPurchase Receipt Item`.`project_name` as \"Project\",\n\t`tabPurchase Receipt Item`.`item_code` as \"Item:Link/Item:120\",\n\t(`tabPurchase Receipt Item`.`qty` - ifnull((select sum(qty) from `tabPurchase Invoice Item` \n\t where `tabPurchase Invoice Item`.purchase_receipt = `tabPurchase Receipt`.name and\n `tabPurchase Invoice Item`.docstatus = 1 and\n\t `tabPurchase Invoice Item`.pr_detail = `tabPurchase Receipt Item`.name), 0))\n\t as \"Qty:Float:110\",\n\t(`tabPurchase Receipt Item`.`base_amount` - ifnull((select sum(base_amount) \n from `tabPurchase Invoice Item` \n where `tabPurchase Invoice Item`.purchase_receipt = `tabPurchase Receipt`.name and\n `tabPurchase Invoice Item`.docstatus = 1 and\n `tabPurchase Invoice Item`.pr_detail = `tabPurchase Receipt Item`.name), 0))\n\t as \"Amount:Currency:110\",\n\t`tabPurchase Receipt Item`.`item_name` as \"Item Name::150\",\n\t`tabPurchase Receipt Item`.`description` as \"Description::200\",\n\t`tabPurchase Receipt`.`company` as \"Company:Link/Company:\"\nfrom `tabPurchase Receipt`, `tabPurchase Receipt Item`\nwhere\n `tabPurchase Receipt`.docstatus = 1 and\n `tabPurchase Receipt`.name = `tabPurchase Receipt Item`.parent and\n (`tabPurchase Receipt Item`.qty > ifnull((select sum(qty) from `tabPurchase Invoice Item` \n where `tabPurchase Invoice Item`.purchase_receipt = `tabPurchase Receipt`.name and\n `tabPurchase Invoice Item`.docstatus=1 and \n `tabPurchase Invoice Item`.pr_detail = `tabPurchase Receipt Item`.name), 0))\norder by `tabPurchase Receipt`.`name` desc", "query": "select\n `tabPurchase Receipt`.`name` as \"Purchase Receipt:Link/Purchase Receipt:120\",\n `tabPurchase Receipt`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Receipt`.`posting_date` as \"Date:Date\",\n\t`tabPurchase Receipt Item`.`project_name` as \"Project\",\n\t`tabPurchase Receipt Item`.`item_code` as \"Item:Link/Item:120\",\n\t(`tabPurchase Receipt Item`.`qty` - ifnull((select sum(qty) from `tabPurchase Invoice Item` \n\t where `tabPurchase Invoice Item`.purchase_receipt = `tabPurchase Receipt`.name and\n `tabPurchase Invoice Item`.docstatus = 1 and\n\t `tabPurchase Invoice Item`.pr_detail = `tabPurchase Receipt Item`.name), 0))\n\t as \"Qty:Float:110\",\n\t(`tabPurchase Receipt Item`.`base_amount` - ifnull((select sum(base_amount) \n from `tabPurchase Invoice Item` \n where `tabPurchase Invoice Item`.purchase_receipt = `tabPurchase Receipt`.name and\n `tabPurchase Invoice Item`.docstatus = 1 and\n `tabPurchase Invoice Item`.pr_detail = `tabPurchase Receipt Item`.name), 0))\n\t as \"Amount:Currency:110\",\n\t`tabPurchase Receipt Item`.`item_name` as \"Item Name::150\",\n\t`tabPurchase Receipt Item`.`description` as \"Description::200\",\n\t`tabPurchase Receipt`.`company` as \"Company:Link/Company:\"\nfrom `tabPurchase Receipt`, `tabPurchase Receipt Item`\nwhere\n `tabPurchase Receipt`.docstatus = 1 and `tabPurchase Receipt`.status != \"Closed\" and \n `tabPurchase Receipt`.name = `tabPurchase Receipt Item`.parent and\n (`tabPurchase Receipt Item`.qty > ifnull((select sum(qty) from `tabPurchase Invoice Item` \n where `tabPurchase Invoice Item`.purchase_receipt = `tabPurchase Receipt`.name and\n `tabPurchase Invoice Item`.docstatus=1 and \n `tabPurchase Invoice Item`.pr_detail = `tabPurchase Receipt Item`.name), 0))\norder by `tabPurchase Receipt`.`name` desc",
"ref_doctype": "Purchase Invoice", "ref_doctype": "Purchase Invoice",
"report_name": "Received Items To Be Billed", "report_name": "Received Items To Be Billed",
"report_type": "Query Report" "report_type": "Query Report"

View File

@ -80,12 +80,12 @@ class PurchaseCommon(BuyingController):
frappe.msgprint(_("Warning: Same item has been entered multiple times.")) frappe.msgprint(_("Warning: Same item has been entered multiple times."))
def check_for_stopped_status(self, doctype, docname): def check_for_stopped_or_closed_status(self, doctype, docname):
stopped = frappe.db.sql("""select name from `tab%s` where name = %s and status = frappe.db.get_value(doctype, docname, "status")
status = 'Stopped'""" % (doctype, '%s'), docname)
if stopped: if status in ("Stopped", "Closed"):
frappe.throw(_("{0} {1} status is 'Stopped'").format(doctype, docname), frappe.InvalidStatusError) frappe.throw(_("{0} {1} status is {2}").format(doctype, docname, status), frappe.InvalidStatusError)
def check_docstatus(self, check, doctype, docname, detail_doctype = ''): def check_docstatus(self, check, doctype, docname, detail_doctype = ''):
if check == 'Next': if check == 'Next':
submitted = frappe.db.sql("""select t1.name from `tab%s` t1,`tab%s` t2 submitted = frappe.db.sql("""select t1.name from `tab%s` t1,`tab%s` t2

View File

@ -19,10 +19,17 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
this._super(); this._super();
// this.frm.dashboard.reset(); // this.frm.dashboard.reset();
if(doc.docstatus == 1 && doc.status != 'Stopped') { if(doc.docstatus == 1 && !in_list(["Stopped", "Closed", "Delivered"], doc.status)) {
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100) if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100) {
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Purchase Order']); cur_frm.add_custom_button(__('Stop'), this.stop_purchase_order);
}
cur_frm.add_custom_button(__('Close'), this.close_purchase_order);
if(doc.delivered_by_supplier && doc.status!="Delivered"){
cur_frm.add_custom_button(__('Mark as Delivered'), this.delivered_by_supplier);
}
if(flt(doc.per_billed)==0) { if(flt(doc.per_billed)==0) {
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry); cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry);
@ -45,8 +52,9 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
cur_frm.cscript.add_from_mappers(); cur_frm.cscript.add_from_mappers();
} }
if(doc.docstatus == 1 && doc.status == 'Stopped') if(doc.docstatus == 1 && in_list(["Stopped", "Closed", "Delivered"], doc.status)) {
cur_frm.add_custom_button(__('Unstop'), cur_frm.cscript['Unstop Purchase Order']); cur_frm.add_custom_button(__('Re-open'), this.unstop_purchase_order);
}
}, },
make_stock_entry: function() { make_stock_entry: function() {
@ -146,15 +154,28 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
make_bank_entry: function() { make_bank_entry: function() {
return frappe.call({ return frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_from_purchase_order", method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_order",
args: { args: {
"purchase_order": cur_frm.doc.name "dt": "Purchase Order",
"dn": cur_frm.doc.name
}, },
callback: function(r) { callback: function(r) {
var doclist = frappe.model.sync(r.message); var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name); frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
} }
}); });
},
stop_purchase_order: function(){
cur_frm.cscript.update_status('Stop', 'Stopped')
},
unstop_purchase_order: function(){
cur_frm.cscript.update_status('Re-open', 'Submitted')
},
close_purchase_order: function(){
cur_frm.cscript.update_status('Close', 'Closed')
},
delivered_by_supplier: function(){
cur_frm.cscript.update_status('Deliver', 'Delivered')
} }
}); });
@ -162,6 +183,17 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
// for backward compatibility: combine new and previous states // for backward compatibility: combine new and previous states
$.extend(cur_frm.cscript, new erpnext.buying.PurchaseOrderController({frm: cur_frm})); $.extend(cur_frm.cscript, new erpnext.buying.PurchaseOrderController({frm: cur_frm}));
cur_frm.cscript.update_status= function(label, status){
frappe.call({
method: "erpnext.buying.doctype.purchase_order.purchase_order.update_status",
args: {status: status, name: cur_frm.doc.name},
callback: function(r) {
cur_frm.set_value("status", status);
cur_frm.reload_doc();
}
})
}
cur_frm.fields_dict['supplier_address'].get_query = function(doc, cdt, cdn) { cur_frm.fields_dict['supplier_address'].get_query = function(doc, cdt, cdn) {
return { return {
filters: {'supplier': doc.supplier} filters: {'supplier': doc.supplier}
@ -201,28 +233,6 @@ cur_frm.cscript.get_last_purchase_rate = function(doc, cdt, cdn){
}); });
} }
cur_frm.cscript['Stop Purchase Order'] = function() {
var doc = cur_frm.doc;
var check = confirm(__("Do you really want to STOP ") + doc.name);
if (check) {
return $c('runserverobj', args={'method':'update_status', 'arg': 'Stopped', 'docs':doc}, function(r,rt) {
cur_frm.refresh();
});
}
}
cur_frm.cscript['Unstop Purchase Order'] = function() {
var doc = cur_frm.doc;
var check = confirm(__("Do you really want to UNSTOP ") + doc.name);
if (check) {
return $c('runserverobj', args={'method':'update_status', 'arg': 'Submitted', 'docs':doc}, function(r,rt) {
cur_frm.refresh();
});
}
}
cur_frm.pformat.indent_no = function(doc, cdt, cdn){ cur_frm.pformat.indent_no = function(doc, cdt, cdn){
//function to make row of table //function to make row of table

View File

@ -151,6 +151,7 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"depends_on": "",
"fieldname": "address_display", "fieldname": "address_display",
"fieldtype": "Small Text", "fieldtype": "Small Text",
"hidden": 1, "hidden": 1,
@ -326,6 +327,255 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "delivered_by_supplier",
"fieldname": "drop_ship",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Drop Ship",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "delivered_by_supplier",
"fieldname": "customer",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Customer",
"no_copy": 0,
"options": "Customer",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "delivered_by_supplier",
"fieldname": "customer_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Customer Name",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "delivered_by_supplier",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "To be delivered to customer",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_19",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "delivered_by_supplier",
"fieldname": "customer_address",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Customer Address",
"no_copy": 0,
"options": "Address",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "delivered_by_supplier",
"fieldname": "customer_contact_person",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Customer Contact",
"no_copy": 0,
"options": "Contact",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "customer_address_display",
"fieldtype": "Small Text",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Customer Address Display",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "customer_contact_display",
"fieldtype": "Small Text",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Customer Contact",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "customer_contact_mobile",
"fieldtype": "Small Text",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Customer Mobile No",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "customer_contact_email",
"fieldtype": "Small Text",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Customer Contact Email",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 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,
@ -1479,7 +1729,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "status", "oldfieldname": "status",
"oldfieldtype": "Select", "oldfieldtype": "Select",
"options": "\nDraft\nTo Receive and Bill\nTo Bill\nTo Receive\nCompleted\nStopped\nCancelled", "options": "\nDraft\nTo Receive and Bill\nTo Bill\nTo Receive\nCompleted\nStopped\nCancelled\nClosed\nDelivered",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1,
@ -2033,7 +2283,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-10-16 06:13:50.058318", "modified": "2015-11-04 04:44:22.025827",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order", "name": "Purchase Order",

View File

@ -39,7 +39,7 @@ class PurchaseOrder(BuyingController):
self.set_status() self.set_status()
pc_obj = frappe.get_doc('Purchase Common') pc_obj = frappe.get_doc('Purchase Common')
pc_obj.validate_for_items(self) pc_obj.validate_for_items(self)
self.check_for_stopped_status(pc_obj) self.check_for_stopped_or_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"])
@ -108,12 +108,12 @@ class PurchaseOrder(BuyingController):
= d.rate = item_last_purchase_rate = d.rate = item_last_purchase_rate
# Check for Stopped status # Check for Stopped status
def check_for_stopped_status(self, pc_obj): def check_for_stopped_or_closed_status(self, pc_obj):
check_list =[] check_list =[]
for d in self.get('items'): for d in self.get('items'):
if d.meta.get_field('prevdoc_docname') and d.prevdoc_docname and d.prevdoc_docname not in check_list: if d.meta.get_field('prevdoc_docname') and d.prevdoc_docname and d.prevdoc_docname not in check_list:
check_list.append(d.prevdoc_docname) check_list.append(d.prevdoc_docname)
pc_obj.check_for_stopped_status( d.prevdoc_doctype, d.prevdoc_docname) pc_obj.check_for_stopped_or_closed_status( d.prevdoc_doctype, d.prevdoc_docname)
def update_requested_qty(self): def update_requested_qty(self):
material_request_map = {} material_request_map = {}
@ -154,14 +154,16 @@ class PurchaseOrder(BuyingController):
def update_status(self, status): def update_status(self, status):
self.check_modified_date() self.check_modified_date()
self.db_set('status', status) self.set_status(update=True, status=status)
self.set_status(update=True)
self.update_requested_qty() self.update_requested_qty()
self.update_ordered_qty() self.update_ordered_qty()
self.notify_update() self.notify_update()
clear_doctype_notifications(self) clear_doctype_notifications(self)
def on_submit(self): def on_submit(self):
if self.delivered_by_supplier == 1:
self.update_status_updater()
super(PurchaseOrder, self).on_submit() super(PurchaseOrder, self).on_submit()
purchase_controller = frappe.get_doc("Purchase Common") purchase_controller = frappe.get_doc("Purchase Common")
@ -176,8 +178,11 @@ class PurchaseOrder(BuyingController):
purchase_controller.update_last_purchase_rate(self, is_submit = 1) purchase_controller.update_last_purchase_rate(self, is_submit = 1)
def on_cancel(self): def on_cancel(self):
if self.delivered_by_supplier == 1:
self.update_status_updater()
pc_obj = frappe.get_doc('Purchase Common') pc_obj = frappe.get_doc('Purchase Common')
self.check_for_stopped_status(pc_obj) self.check_for_stopped_or_closed_status(pc_obj)
# Check if Purchase Receipt has been submitted against current Purchase Order # Check if Purchase Receipt has been submitted against current Purchase Order
pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Receipt', docname = self.name, detail_doctype = 'Purchase Receipt Item') pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Receipt', docname = self.name, detail_doctype = 'Purchase Receipt Item')
@ -214,6 +219,28 @@ class PurchaseOrder(BuyingController):
"prevdoc_detail_docname", "supplier_quotation", "supplier_quotation_item"): "prevdoc_detail_docname", "supplier_quotation", "supplier_quotation_item"):
d.set(field, None) d.set(field, None)
def update_status_updater(self):
self.status_updater[0].update({
"target_parent_dt": "Sales Order",
"target_dt": "Sales Order Item",
'target_field': 'ordered_qty',
"target_parent_field": ''
})
def update_delivered_qty_in_sales_order(self):
"""Update delivered qty in Sales Order for drop ship"""
sales_orders_to_update = []
for item in self.items:
if item.prevdoc_doctype == "Sales Order":
if item.prevdoc_docname not in sales_orders_to_update:
sales_orders_to_update.append(item.prevdoc_docname)
for so_name in sales_orders_to_update:
so = frappe.get_doc("Sales Order", so_name)
so.update_delivery_status(self.name)
so.set_status(update=True)
so.notify_update()
@frappe.whitelist() @frappe.whitelist()
def stop_or_unstop_purchase_orders(names, status): def stop_or_unstop_purchase_orders(names, status):
if not frappe.has_permission("Purchase Order", "write"): if not frappe.has_permission("Purchase Order", "write"):
@ -223,11 +250,11 @@ def stop_or_unstop_purchase_orders(names, status):
for name in names: for name in names:
po = frappe.get_doc("Purchase Order", name) po = frappe.get_doc("Purchase Order", name)
if po.docstatus == 1: if po.docstatus == 1:
if status=="Stopped": if status in ("Stopped", "Closed"):
if po.status not in ("Stopped", "Cancelled") and (po.per_received < 100 or po.per_billed < 100): if po.status not in ("Stopped", "Cancelled", "Closed") and (po.per_received < 100 or po.per_billed < 100):
po.update_status("Stopped") po.update_status(status)
else: else:
if po.status == "Stopped": if po.status in ("Stopped", "Closed"):
po.update_status("Draft") po.update_status("Draft")
frappe.local.message_log = [] frappe.local.message_log = []
@ -325,3 +352,10 @@ def make_stock_entry(purchase_order, item_code):
stock_entry.bom_no = po_item.bom stock_entry.bom_no = po_item.bom
stock_entry.get_items() stock_entry.get_items()
return stock_entry.as_dict() return stock_entry.as_dict()
@frappe.whitelist()
def update_status(status, name):
po = frappe.get_doc("Purchase Order", name)
po.update_status(status)
po.update_delivered_qty_in_sales_order()

View File

@ -4,7 +4,11 @@ frappe.listview_settings['Purchase Order'] = {
get_indicator: function(doc) { get_indicator: function(doc) {
if(doc.status==="Stopped") { if(doc.status==="Stopped") {
return [__("Stopped"), "darkgrey", "status,=,Stopped"]; return [__("Stopped"), "darkgrey", "status,=,Stopped"];
} else if(flt(doc.per_received, 2) < 100 && doc.status!=="Stopped") { } else if(doc.status==="Closed"){
return [__("Closed"), "green", "status,=,Closed"];
} else if (doc.status==="Delivered") {
return [__("Delivered"), "green", "status,=,Closed"];
}else if(flt(doc.per_received, 2) < 100 && doc.status!=="Stopped") {
if(flt(doc.per_billed, 2) < 100) { if(flt(doc.per_billed, 2) < 100) {
return [__("To Receive and Bill"), "orange", return [__("To Receive and Bill"), "orange",
"per_received,<,100|per_billed,<,100|status,!=,Stopped"]; "per_received,<,100|per_billed,<,100|status,!=,Stopped"];
@ -21,13 +25,16 @@ frappe.listview_settings['Purchase Order'] = {
onload: function(listview) { onload: function(listview) {
var method = "erpnext.buying.doctype.purchase_order.purchase_order.stop_or_unstop_purchase_orders"; var method = "erpnext.buying.doctype.purchase_order.purchase_order.stop_or_unstop_purchase_orders";
listview.page.add_menu_item(__("Set as Stopped"), function() { listview.page.add_menu_item(__("Close"), function() {
listview.call_for_selected_items(method, {"status": "Closed"});
});
listview.page.add_menu_item(__("Stop"), function() {
listview.call_for_selected_items(method, {"status": "Stopped"}); listview.call_for_selected_items(method, {"status": "Stopped"});
}); });
listview.page.add_menu_item(__("Set as Unstopped"), function() { listview.page.add_menu_item(__("Re-open"), function() {
listview.call_for_selected_items(method, {"status": "Submitted"}); listview.call_for_selected_items(method, {"status": "Submitted"});
}); });
} }
}; };

View File

@ -70,6 +70,20 @@ class TestPurchaseOrder(unittest.TestCase):
from erpnext.utilities.transaction_base import UOMMustBeIntegerError from erpnext.utilities.transaction_base import UOMMustBeIntegerError
po = create_purchase_order(qty=3.4, do_not_save=True) po = create_purchase_order(qty=3.4, do_not_save=True)
self.assertRaises(UOMMustBeIntegerError, po.insert) self.assertRaises(UOMMustBeIntegerError, po.insert)
def test_ordered_qty_for_closing_po(self):
bin = frappe.get_all("Bin", filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
fields=["ordered_qty"])
existing_ordered_qty = bin[0].ordered_qty if bin else 0.0
po = create_purchase_order(item_code= "_Test Item", qty=1)
self.assertEquals(get_ordered_qty(item_code= "_Test Item", warehouse="_Test Warehouse - _TC"), existing_ordered_qty+1)
po.update_status("Closed")
self.assertEquals(get_ordered_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"), existing_ordered_qty)
def create_purchase_order(**args): def create_purchase_order(**args):
po = frappe.new_doc("Purchase Order") po = frappe.new_doc("Purchase Order")

View File

View File

@ -0,0 +1,17 @@
{
"creation": "2015-10-20 16:46:39.382799",
"custom_format": 0,
"disabled": 0,
"doc_type": "Purchase Order",
"docstatus": 0,
"doctype": "Print Format",
"font": "Default",
"format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"HTML\", \"options\": \"<h1 style=\\\"text-align: center;\\\">Purchase Order</h1><div style=\\\"text-align: center;\\\">{{doc.name}}</div><div style=\\\"text-align: center;\\\"><hr></div>\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"title\"}, {\"print_hide\": 0, \"fieldname\": \"supplier\"}, {\"print_hide\": 0, \"fieldname\": \"supplier_name\"}, {\"print_hide\": 0, \"fieldname\": \"address_display\"}, {\"print_hide\": 0, \"fieldname\": \"contact_display\"}, {\"print_hide\": 0, \"fieldname\": \"contact_mobile\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"transaction_date\"}, {\"print_hide\": 0, \"fieldname\": \"customer\"}, {\"print_hide\": 0, \"fieldname\": \"customer_name\"}, {\"print_hide\": 0, \"fieldname\": \"customer_address_display\"}, {\"print_hide\": 0, \"fieldname\": \"customer_contact_display\"}, {\"print_hide\": 0, \"fieldname\": \"customer_contact_mobile\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"item_code\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"300px\"}, {\"print_hide\": 0, \"fieldname\": \"image\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"qty\", \"print_width\": \"60px\"}, {\"print_hide\": 0, \"fieldname\": \"uom\", \"print_width\": \"100px\"}, {\"print_hide\": 0, \"fieldname\": \"discount_percentage\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"pricing_rule\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"supplier_quotation\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"supplier_quotation_item\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"items\"}, {\"print_hide\": 0, \"fieldname\": \"get_last_purchase_rate\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"total\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"category\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"add_deduct_tax\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"charge_type\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"row_id\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"included_in_print_rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"account_head\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"cost_center\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"description\", \"print_width\": \"300px\"}, {\"print_hide\": 0, \"fieldname\": \"rate\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"tax_amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"total\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"taxes\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"grand_total\"}, {\"print_hide\": 0, \"fieldname\": \"in_words\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"terms\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"customer_contact_person\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"recurring_print_format\"}]",
"modified": "2015-10-20 17:21:45.810640",
"modified_by": "saurabh@erpnext.com",
"name": "Drop Shipping",
"owner": "Administrator",
"print_format_builder": 1,
"print_format_type": "Server",
"standard": "No"
}

View File

@ -0,0 +1,7 @@
- **Desktop Reorganization:** To Do, Calendar, Messages, Notes, Activty have been moved into module **Tools**
- Integrations and Installer has been moved into **Setup**
- Make Purchase Order from Sales Order if Supplier is mentioned.
- **Drop Ship Integration:** Make Sales Order with item marked as **Supplier delivers to Customer** and then make a Purchase Order from the Sales Order with Supplier details.
- Customer details in Purchase Order for Drop Ship.
- Set Sales Order, Purchase Order, Delivery Note and Purchase Receipt as **Closed** to clear notifications.
- Allocate leaves in **Leave Allocation** by specific dates and not Fiscal Year. Sponsored by [Believer's Church](https://www.believerschurch.com)

View File

@ -64,9 +64,9 @@ def get_data():
"type": "module" "type": "module"
}, },
"Learn": { "Learn": {
"color": "#FCB868", "color": "#FF888B",
"force_show": True, "force_show": True,
"icon": "icon-facetime-video", "icon": "octicon octicon-device-camera-video",
"type": "module", "type": "module",
"is_help": True "is_help": True
} }

View File

@ -7,6 +7,11 @@ def get_data():
"label": _("Documents"), "label": _("Documents"),
"icon": "icon-star", "icon": "icon-star",
"items": [ "items": [
{
"type": "doctype",
"name": "Item",
"description": _("All Products or Services."),
},
{ {
"type": "doctype", "type": "doctype",
"name": "Material Request", "name": "Material Request",
@ -32,11 +37,6 @@ def get_data():
"name": "Installation Note", "name": "Installation Note",
"description": _("Installation record for a Serial No.") "description": _("Installation record for a Serial No.")
}, },
{
"type": "doctype",
"name": "Item",
"description": _("All Products or Services."),
},
{ {
"type": "doctype", "type": "doctype",
"name": "Warehouse", "name": "Warehouse",

View File

@ -216,7 +216,7 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len,
return frappe.db.sql("""select `tabDelivery Note`.name, `tabDelivery Note`.customer_name return frappe.db.sql("""select `tabDelivery Note`.name, `tabDelivery Note`.customer_name
from `tabDelivery Note` from `tabDelivery Note`
where `tabDelivery Note`.`%(key)s` like %(txt)s and where `tabDelivery Note`.`%(key)s` like %(txt)s and
`tabDelivery Note`.docstatus = 1 %(fcond)s and `tabDelivery Note`.docstatus = 1 and status not in ("Stopped", "Closed") %(fcond)s and
(ifnull((select sum(qty) from `tabDelivery Note Item` where (ifnull((select sum(qty) from `tabDelivery Note Item` where
`tabDelivery Note Item`.parent=`tabDelivery Note`.name), 0) > `tabDelivery Note Item`.parent=`tabDelivery Note`.name), 0) >
ifnull((select sum(qty) from `tabSales Invoice Item` where ifnull((select sum(qty) from `tabSales Invoice Item` where

View File

@ -163,7 +163,7 @@ def validate_recurring_document(doc):
raise_exception=1) raise_exception=1)
elif not (doc.from_date and doc.to_date): elif not (doc.from_date and doc.to_date):
throw(_("Period From and Period To dates mandatory for recurring %s") % doc.doctype) throw(_("Period From and Period To dates mandatory for recurring {0}").format(doc.doctype))
# #
def convert_to_recurring(doc, posting_date): def convert_to_recurring(doc, posting_date):

View File

@ -219,12 +219,12 @@ class SellingController(StockController):
so_warehouse = so_item and so_item[0]["warehouse"] or "" so_warehouse = so_item and so_item[0]["warehouse"] or ""
return so_qty, so_warehouse return so_qty, so_warehouse
def check_stop_sales_order(self, ref_fieldname): def check_stop_or_close_sales_order(self, ref_fieldname):
for d in self.get("items"): for d in self.get("items"):
if d.get(ref_fieldname): if d.get(ref_fieldname):
status = frappe.db.get_value("Sales Order", d.get(ref_fieldname), "status") status = frappe.db.get_value("Sales Order", d.get(ref_fieldname), "status")
if status == "Stopped": if status in ("Stopped", "Closed"):
frappe.throw(_("Sales Order {0} is stopped").format(d.get(ref_fieldname))) frappe.throw(_("Sales Order {0} is {1}").format(d.get(ref_fieldname), status))
def check_active_sales_items(obj): def check_active_sales_items(obj):
for d in obj.get("items"): for d in obj.get("items"):

View File

@ -37,6 +37,7 @@ status_map = {
["Completed", "eval:self.order_type == 'Maintenance' and self.per_billed == 100 and self.docstatus == 1"], ["Completed", "eval:self.order_type == 'Maintenance' and self.per_billed == 100 and self.docstatus == 1"],
["Stopped", "eval:self.status=='Stopped'"], ["Stopped", "eval:self.status=='Stopped'"],
["Cancelled", "eval:self.docstatus==2"], ["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed'"],
], ],
"Purchase Order": [ "Purchase Order": [
["Draft", None], ["Draft", None],
@ -44,18 +45,22 @@ status_map = {
["To Bill", "eval:self.per_received == 100 and self.per_billed < 100 and self.docstatus == 1"], ["To Bill", "eval:self.per_received == 100 and self.per_billed < 100 and self.docstatus == 1"],
["To Receive", "eval:self.per_received < 100 and self.per_billed == 100 and self.docstatus == 1"], ["To Receive", "eval:self.per_received < 100 and self.per_billed == 100 and self.docstatus == 1"],
["Completed", "eval:self.per_received == 100 and self.per_billed == 100 and self.docstatus == 1"], ["Completed", "eval:self.per_received == 100 and self.per_billed == 100 and self.docstatus == 1"],
["Delivered", "eval:self.status=='Delivered'"],
["Stopped", "eval:self.status=='Stopped'"], ["Stopped", "eval:self.status=='Stopped'"],
["Cancelled", "eval:self.docstatus==2"], ["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed'"],
], ],
"Delivery Note": [ "Delivery Note": [
["Draft", None], ["Draft", None],
["Submitted", "eval:self.docstatus==1"], ["Submitted", "eval:self.docstatus==1"],
["Cancelled", "eval:self.docstatus==2"], ["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed'"],
], ],
"Purchase Receipt": [ "Purchase Receipt": [
["Draft", None], ["Draft", None],
["Submitted", "eval:self.docstatus==1"], ["Submitted", "eval:self.docstatus==1"],
["Cancelled", "eval:self.docstatus==2"], ["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed'"],
] ]
} }
@ -71,12 +76,16 @@ class StatusUpdater(Document):
self.update_qty() self.update_qty()
self.validate_qty() self.validate_qty()
def set_status(self, update=False): def set_status(self, update=False, status=None):
if self.is_new(): if self.is_new():
return return
if self.doctype in status_map: if self.doctype in status_map:
_status = self.status _status = self.status
if status and update:
self.db_set("status", status)
sl = status_map[self.doctype][:] sl = status_map[self.doctype][:]
sl.reverse() sl.reverse()
for s in sl: for s in sl:
@ -168,7 +177,6 @@ class StatusUpdater(Document):
else: else:
args['cond'] = ' and parent!="%s"' % self.name.replace('"', '\"') args['cond'] = ' and parent!="%s"' % self.name.replace('"', '\"')
args['set_modified'] = ''
if change_modified: if change_modified:
args['set_modified'] = ', modified = now(), modified_by = "{0}"'\ args['set_modified'] = ', modified = now(), modified_by = "{0}"'\
.format(frappe.db.escape(frappe.session.user)) .format(frappe.db.escape(frappe.session.user))
@ -252,9 +260,9 @@ class StatusUpdater(Document):
zero_amount_refdoc.append(item.get(ref_fieldname)) zero_amount_refdoc.append(item.get(ref_fieldname))
if zero_amount_refdoc: if zero_amount_refdoc:
self.update_biling_status(zero_amount_refdoc, ref_dt, ref_fieldname) self.update_billing_status(zero_amount_refdoc, ref_dt, ref_fieldname)
def update_biling_status(self, zero_amount_refdoc, ref_dt, ref_fieldname): def update_billing_status(self, zero_amount_refdoc, ref_dt, ref_fieldname):
for ref_dn in zero_amount_refdoc: for ref_dn in zero_amount_refdoc:
ref_doc_qty = flt(frappe.db.sql("""select sum(ifnull(qty, 0)) from `tab%s Item` ref_doc_qty = flt(frappe.db.sql("""select sum(ifnull(qty, 0)) from `tab%s Item`
where parent=%s""" % (ref_dt, '%s'), (ref_dn))[0][0]) where parent=%s""" % (ref_dt, '%s'), (ref_dn))[0][0])

View File

@ -29,8 +29,8 @@ blogs.
""" """
app_icon = "icon-th" app_icon = "icon-th"
app_color = "#e74c3c" app_color = "#e74c3c"
app_version = "6.6.7" app_version = "6.7.0"
github_link = "https://github.com/frappe/erpnext" source_link = "https://github.com/frappe/erpnext"
error_report_email = "support@erpnext.com" error_report_email = "support@erpnext.com"

View File

@ -78,25 +78,22 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "leave_type", "fieldname": "column_break1",
"fieldtype": "Link", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 1, "in_filter": 0,
"in_list_view": 1, "in_list_view": 0,
"label": "Leave Type",
"no_copy": 0, "no_copy": 0,
"oldfieldname": "leave_type",
"oldfieldtype": "Link",
"options": "Leave Type",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 0,
"search_index": 1, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0,
"width": "50%"
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@ -126,62 +123,38 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "column_break1", "fieldname": "section_break_6",
"fieldtype": "Column Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0,
"width": "50%"
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"default": "Today",
"fieldname": "posting_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Posting Date",
"no_copy": 1,
"oldfieldname": "date",
"oldfieldtype": "Date",
"permlevel": 0,
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "fiscal_year", "fieldname": "leave_type",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 1, "in_filter": 1,
"in_list_view": 0, "in_list_view": 1,
"label": "Fiscal Year", "label": "Leave Type",
"no_copy": 0, "no_copy": 0,
"oldfieldname": "fiscal_year", "oldfieldname": "leave_type",
"oldfieldtype": "Data", "oldfieldtype": "Link",
"options": "Fiscal Year", "options": "Leave Type",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
@ -195,19 +168,20 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "carry_forward", "fieldname": "from_date",
"fieldtype": "Check", "fieldtype": "Date",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Carry Forward", "label": "From Date",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 1,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
@ -216,18 +190,39 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"depends_on": "carry_forward", "fieldname": "to_date",
"fieldname": "carry_forwarded_leaves", "fieldtype": "Date",
"fieldtype": "Float",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Carry Forwarded Leaves", "label": "To Date",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 1, "read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_10",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -255,6 +250,50 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"description": "",
"fieldname": "carry_forward",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Add unused leaves from previous allocations",
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "carry_forward",
"fieldname": "carry_forwarded_leaves",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Unused leaves",
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
"bold": 0, "bold": 0,
@ -310,7 +349,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-10-02 07:38:55.314632", "modified": "2015-11-04 03:13:11.121463",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Leave Allocation", "name": "Leave Allocation",
@ -359,7 +398,7 @@
], ],
"read_only": 0, "read_only": 0,
"read_only_onload": 0, "read_only_onload": 0,
"search_fields": "employee,employee_name,leave_type,total_leaves_allocated,fiscal_year", "search_fields": "employee,employee_name,leave_type,total_leaves_allocated",
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC" "sort_order": "DESC"
} }

View File

@ -3,13 +3,14 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cint, flt from frappe.utils import cint, flt, date_diff
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.hr.utils import set_employee_name from erpnext.hr.utils import set_employee_name
class LeaveAllocation(Document): class LeaveAllocation(Document):
def validate(self): def validate(self):
self.validate_period()
self.validate_new_leaves_allocated_value() self.validate_new_leaves_allocated_value()
self.check_existing_leave_allocation() self.check_existing_leave_allocation()
if not self.total_leaves_allocated: if not self.total_leaves_allocated:
@ -23,6 +24,10 @@ class LeaveAllocation(Document):
def on_update(self): def on_update(self):
self.get_total_allocated_leaves() self.get_total_allocated_leaves()
def validate_period(self):
if date_diff(self.to_date, self.from_date) <= 0:
frappe.throw(_("Invalid period"))
def validate_new_leaves_allocated_value(self): def validate_new_leaves_allocated_value(self):
"""validate that leave allocation is in multiples of 0.5""" """validate that leave allocation is in multiples of 0.5"""
if flt(self.new_leaves_allocated) % 0.5: if flt(self.new_leaves_allocated) % 0.5:
@ -31,28 +36,29 @@ class LeaveAllocation(Document):
def check_existing_leave_allocation(self): def check_existing_leave_allocation(self):
"""check whether leave for same type is already allocated or not""" """check whether leave for same type is already allocated or not"""
leave_allocation = frappe.db.sql("""select name from `tabLeave Allocation` leave_allocation = frappe.db.sql("""select name from `tabLeave Allocation`
where employee=%s and leave_type=%s and fiscal_year=%s and docstatus=1""", where employee='%s' and leave_type='%s' and to_date >= '%s' and from_date <= '%s' and docstatus=1
(self.employee, self.leave_type, self.fiscal_year)) """%(self.employee, self.leave_type, self.from_date, self.to_date))
if leave_allocation: if leave_allocation:
frappe.msgprint(_("Leaves for type {0} already allocated for Employee {1} for Fiscal Year {0}").format(self.leave_type, frappe.msgprint(_("Leaves for type {0} already allocated for Employee {1} for period {2} - {3}").format(self.leave_type,
self.employee, self.fiscal_year)) self.employee, self.from_date, self.to_date))
frappe.throw('<a href="#Form/Leave Allocation/{0}">{0}</a>'.format(leave_allocation[0][0])) frappe.throw(_('Reference') + ': <a href="#Form/Leave Allocation/{0}">{0}</a>'.format(leave_allocation[0][0]))
def get_leave_bal(self, prev_fyear): def get_leave_bal(self):
return self.get_leaves_allocated(prev_fyear) - self.get_leaves_applied(prev_fyear) return self.get_leaves_allocated() - self.get_leaves_applied()
def get_leaves_applied(self, fiscal_year): def get_leaves_applied(self):
leaves_applied = frappe.db.sql("""select SUM(ifnull(total_leave_days, 0)) leaves_applied = frappe.db.sql("""select SUM(ifnull(total_leave_days, 0))
from `tabLeave Application` where employee=%s and leave_type=%s from `tabLeave Application` where employee=%s and leave_type=%s
and fiscal_year=%s and docstatus=1""", and to_date<=%s and docstatus=1""",
(self.employee, self.leave_type, fiscal_year)) (self.employee, self.leave_type, self.from_date))
return leaves_applied and flt(leaves_applied[0][0]) or 0 return leaves_applied and flt(leaves_applied[0][0]) or 0
def get_leaves_allocated(self, fiscal_year): def get_leaves_allocated(self):
leaves_allocated = frappe.db.sql("""select SUM(ifnull(total_leaves_allocated, 0)) leaves_allocated = frappe.db.sql("""select SUM(ifnull(total_leaves_allocated, 0))
from `tabLeave Allocation` where employee=%s and leave_type=%s from `tabLeave Allocation` where employee=%s and leave_type=%s
and fiscal_year=%s and docstatus=1 and name!=%s""", and to_date<=%s and docstatus=1 and name!=%s""",
(self.employee, self.leave_type, fiscal_year, self.name)) (self.employee, self.leave_type, self.from_date, self.name))
return leaves_allocated and flt(leaves_allocated[0][0]) or 0 return leaves_allocated and flt(leaves_allocated[0][0]) or 0
def allow_carry_forward(self): def allow_carry_forward(self):
@ -67,14 +73,11 @@ class LeaveAllocation(Document):
def get_carry_forwarded_leaves(self): def get_carry_forwarded_leaves(self):
if self.carry_forward: if self.carry_forward:
self.allow_carry_forward() self.allow_carry_forward()
prev_fiscal_year = frappe.db.sql("""select name from `tabFiscal Year`
where year_start_date = (select date_add(year_start_date, interval -1 year)
from `tabFiscal Year` where name=%s)
order by name desc limit 1""", self.fiscal_year)
prev_fiscal_year = prev_fiscal_year and prev_fiscal_year[0][0] or ''
prev_bal = 0 prev_bal = 0
if prev_fiscal_year and cint(self.carry_forward) == 1: if cint(self.carry_forward) == 1:
prev_bal = self.get_leave_bal(prev_fiscal_year) prev_bal = self.get_leave_bal()
ret = { ret = {
'carry_forwarded_leaves': prev_bal, 'carry_forwarded_leaves': prev_bal,
'total_leaves_allocated': flt(prev_bal) + flt(self.new_leaves_allocated) 'total_leaves_allocated': flt(prev_bal) + flt(self.new_leaves_allocated)
@ -83,6 +86,10 @@ class LeaveAllocation(Document):
def get_total_allocated_leaves(self): def get_total_allocated_leaves(self):
leave_det = self.get_carry_forwarded_leaves() leave_det = self.get_carry_forwarded_leaves()
self.validate_total_leaves_allocated(leave_det)
frappe.db.set(self,'carry_forwarded_leaves',flt(leave_det['carry_forwarded_leaves'])) frappe.db.set(self,'carry_forwarded_leaves',flt(leave_det['carry_forwarded_leaves']))
frappe.db.set(self,'total_leaves_allocated',flt(leave_det['total_leaves_allocated'])) frappe.db.set(self,'total_leaves_allocated',flt(leave_det['total_leaves_allocated']))
def validate_total_leaves_allocated(self, leave_det):
if date_diff(self.to_date, self.from_date) <= leave_det['total_leaves_allocated']:
frappe.throw(_("Total allocated leaves are more than period"))

View File

@ -1,4 +1,69 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
import unittest
from frappe.utils import getdate
test_records = frappe.get_test_records('Leave Allocation') class TestLeaveAllocation(unittest.TestCase):
def test_overlapping_allocation(self):
employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0])
leaves = [
{
"doctype": "Leave Allocation",
"__islocal": 1,
"employee": employee.name,
"employee_name": employee.employee_name,
"leave_type": "_Test Leave Type",
"from_date": getdate("2015-10-1"),
"to_date": getdate("2015-10-31"),
"new_leaves_allocated": 5,
"docstatus": 1
},
{
"doctype": "Leave Allocation",
"__islocal": 1,
"employee": employee.name,
"employee_name": employee.employee_name,
"leave_type": "_Test Leave Type",
"from_date": getdate("2015-09-1"),
"to_date": getdate("2015-11-30"),
"new_leaves_allocated": 5
}
]
frappe.get_doc(leaves[0]).save()
self.assertRaises(frappe.ValidationError, frappe.get_doc(leaves[1]).save)
def test_invalid_period(self):
employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0])
d = frappe.get_doc({
"doctype": "Leave Allocation",
"__islocal": 1,
"employee": employee.name,
"employee_name": employee.employee_name,
"leave_type": "_Test Leave Type",
"from_date": getdate("2015-09-30"),
"to_date": getdate("2015-09-1"),
"new_leaves_allocated": 5
})
#invalid period
self.assertRaises(frappe.ValidationError, d.save)
def test_allocated_leave_days_over_period(self):
employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0])
d = frappe.get_doc({
"doctype": "Leave Allocation",
"__islocal": 1,
"employee": employee.name,
"employee_name": employee.employee_name,
"leave_type": "_Test Leave Type",
"from_date": getdate("2015-09-1"),
"to_date": getdate("2015-09-30"),
"new_leaves_allocated": 35
})
#allocated leave more than period
self.assertRaises(frappe.ValidationError, d.save)
test_dependencies = ["Employee", "Leave Type"]

View File

@ -1,18 +1,20 @@
[ [
{ {
"docstatus": 1, "docstatus": 1,
"doctype": "Leave Allocation", "doctype": "Leave Allocation",
"employee": "_T-Employee-0001", "employee": "_T-Employee-0001",
"fiscal_year": "_Test Fiscal Year 2013", "from_date": "2013-01-01",
"leave_type": "_Test Leave Type", "to_date": "2013-12-31",
"new_leaves_allocated": 15 "leave_type": "_Test Leave Type",
}, "new_leaves_allocated": 15
{ },
"docstatus": 1, {
"doctype": "Leave Allocation", "docstatus": 1,
"employee": "_T-Employee-0002", "doctype": "Leave Allocation",
"fiscal_year": "_Test Fiscal Year 2013", "employee": "_T-Employee-0002",
"leave_type": "_Test Leave Type", "from_date": "2013-01-01",
"new_leaves_allocated": 15 "to_date": "2013-12-31",
} "leave_type": "_Test Leave Type",
] "new_leaves_allocated": 15
}
]

View File

@ -28,21 +28,6 @@ frappe.ui.form.on("Leave Application", {
frm.set_value("status", "Open"); frm.set_value("status", "Open");
frm.trigger("calculate_total_days"); frm.trigger("calculate_total_days");
} }
frm.set_intro("");
if (frm.is_new() && !in_list(user_roles, "HR User")) {
frm.set_intro(__("Fill the form and save it"));
} else {
if(frm.doc.docstatus==0 && frm.doc.status=="Open") {
if(user==frm.doc.leave_approver) {
frm.set_intro(__("You are the Leave Approver for this record. Please Update the 'Status' and Save"));
frm.toggle_enable("status", true);
} else {
frm.set_intro(__("This Leave Application is pending approval. Only the Leave Approver can update status."))
frm.toggle_enable("status", false);
}
}
}
}, },
leave_approver: function(frm) { leave_approver: function(frm) {
@ -53,10 +38,6 @@ frappe.ui.form.on("Leave Application", {
frm.trigger("get_leave_balance"); frm.trigger("get_leave_balance");
}, },
fiscal_year: function(frm) {
frm.trigger("get_leave_balance");
},
leave_type: function(frm) { leave_type: function(frm) {
frm.trigger("get_leave_balance"); frm.trigger("get_leave_balance");
}, },
@ -85,12 +66,13 @@ frappe.ui.form.on("Leave Application", {
}, },
get_leave_balance: function(frm) { get_leave_balance: function(frm) {
if(frm.doc.docstatus==0 && frm.doc.employee && frm.doc.leave_type && frm.doc.fiscal_year) { if(frm.doc.docstatus==0 && frm.doc.employee && frm.doc.leave_type && frm.doc.from_date && frm.doc.to_date) {
return frm.call({ return frm.call({
method: "get_leave_balance", method: "get_leave_balance",
args: { args: {
employee: frm.doc.employee, employee: frm.doc.employee,
fiscal_year: frm.doc.fiscal_year, from_date: frm.doc.from_date,
to_date: frm.doc.to_date,
leave_type: frm.doc.leave_type leave_type: frm.doc.leave_type
} }
}); });
@ -109,6 +91,7 @@ frappe.ui.form.on("Leave Application", {
callback: function(response) { callback: function(response) {
if (response && response.message) { if (response && response.message) {
frm.set_value('total_leave_days', response.message.total_leave_days); frm.set_value('total_leave_days', response.message.total_leave_days);
frm.trigger("get_leave_balance");
} }
} }
}); });

View File

@ -21,7 +21,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Status", "label": "Status",
"no_copy": 1, "no_copy": 1,
"options": "Open\nApproved\nRejected", "options": "Open\nApproved\nRejected",
"permlevel": 1, "permlevel": 1,
@ -559,7 +559,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 3, "max_attachments": 3,
"modified": "2015-10-02 07:38:55.471712", "modified": "2015-10-28 16:14:25.640730",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Leave Application", "name": "Leave Application",

View File

@ -100,7 +100,7 @@ class LeaveApplication(Document):
if not is_lwp(self.leave_type): if not is_lwp(self.leave_type):
self.leave_balance = get_leave_balance(self.employee, self.leave_balance = get_leave_balance(self.employee,
self.leave_type, self.fiscal_year)["leave_balance"] self.leave_type, self.from_date, self.to_date)["leave_balance"]
if self.status != "Rejected" \ if self.status != "Rejected" \
and self.leave_balance - self.total_leave_days < 0: and self.leave_balance - self.total_leave_days < 0:
@ -122,9 +122,8 @@ class LeaveApplication(Document):
employee = %(employee)s employee = %(employee)s
and docstatus < 2 and docstatus < 2
and status in ("Open", "Approved") and status in ("Open", "Approved")
and (from_date between %(from_date)s and %(to_date)s and to_date >= %(from_date)s
or to_date between %(from_date)s and %(to_date)s and from_date <= %(to_date)s
or %(from_date)s between from_date and to_date)
and name != %(name)s""", { and name != %(name)s""", {
"employee": self.employee, "employee": self.employee,
"from_date": self.from_date, "from_date": self.from_date,
@ -251,18 +250,18 @@ def get_total_leave_days(leave_app):
return ret return ret
@frappe.whitelist() @frappe.whitelist()
def get_leave_balance(employee, leave_type, fiscal_year): def get_leave_balance(employee, leave_type, from_date, to_date):
leave_all = frappe.db.sql("""select total_leaves_allocated leave_all = frappe.db.sql("""select total_leaves_allocated
from `tabLeave Allocation` where employee = %s and leave_type = %s from `tabLeave Allocation` where employee = %s and leave_type = %s
and fiscal_year = %s and docstatus = 1""", (employee, and from_date<=%s and to_date>=%s and docstatus = 1""", (employee,
leave_type, fiscal_year)) leave_type, from_date, to_date))
leave_all = leave_all and flt(leave_all[0][0]) or 0 leave_all = leave_all and flt(leave_all[0][0]) or 0
leave_app = frappe.db.sql("""select SUM(total_leave_days) leave_app = frappe.db.sql("""select SUM(total_leave_days)
from `tabLeave Application` from `tabLeave Application`
where employee = %s and leave_type = %s and fiscal_year = %s where employee = %s and leave_type = %s and to_date>=%s and from_date<=%s
and status="Approved" and docstatus = 1""", (employee, leave_type, fiscal_year)) and status="Approved" and docstatus = 1""", (employee, leave_type, from_date, to_date))
leave_app = leave_app and flt(leave_app[0][0]) or 0 leave_app = leave_app and flt(leave_app[0][0]) or 0
ret = {'leave_balance': leave_all - leave_app} ret = {'leave_balance': leave_all - leave_app}

View File

@ -145,16 +145,38 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "fiscal_year", "fieldname": "from_date",
"fieldtype": "Link", "fieldtype": "Date",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 1, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Fiscal Year", "label": "From Date",
"no_copy": 0, "no_copy": 0,
"options": "Fiscal Year",
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "to_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "To Date",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
@ -260,7 +282,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 1, "issingle": 1,
"istable": 0, "istable": 0,
"modified": "2015-06-05 11:38:19.994852", "modified": "2015-10-28 16:23:57.733900",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Leave Control Panel", "name": "Leave Control Panel",

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cint, cstr, flt, nowdate, comma_and from frappe.utils import cint, cstr, flt, nowdate, comma_and, date_diff
from frappe import msgprint, _ from frappe import msgprint, _
from frappe.model.document import Document from frappe.model.document import Document
@ -27,9 +27,13 @@ class LeaveControlPanel(Document):
return e return e
def validate_values(self): def validate_values(self):
for f in ["fiscal_year", "leave_type", "no_of_days"]: for f in ["from_date", "to_date", "leave_type", "no_of_days"]:
if not self.get(f): if not self.get(f):
frappe.throw(_("{0} is required").format(self.meta.get_label(f))) frappe.throw(_("{0} is required").format(self.meta.get_label(f)))
def to_date_validation(self):
if date_diff(self.to_date, self.from_date) <= 0:
return "Invalid period"
def allocate_leave(self): def allocate_leave(self):
self.validate_values() self.validate_values()
@ -45,8 +49,8 @@ class LeaveControlPanel(Document):
la.employee = cstr(d[0]) la.employee = cstr(d[0])
la.employee_name = frappe.db.get_value('Employee',cstr(d[0]),'employee_name') la.employee_name = frappe.db.get_value('Employee',cstr(d[0]),'employee_name')
la.leave_type = self.leave_type la.leave_type = self.leave_type
la.fiscal_year = self.fiscal_year la.from_date = self.from_date
la.posting_date = nowdate() la.to_date = self.to_date
la.carry_forward = cint(self.carry_forward) la.carry_forward = cint(self.carry_forward)
la.new_leaves_allocated = flt(self.no_of_days) la.new_leaves_allocated = flt(self.no_of_days)
la.docstatus = 1 la.docstatus = 1

View File

@ -25,8 +25,8 @@ cur_frm.cscript.create_salary_slip = function(doc, cdt, cdn) {
cur_frm.cscript.submit_salary_slip = function(doc, cdt, cdn) { cur_frm.cscript.submit_salary_slip = function(doc, cdt, cdn) {
cur_frm.cscript.display_activity_log(""); cur_frm.cscript.display_activity_log("");
var check = confirm(__("Do you really want to Submit all Salary Slip for month {0} and year {1}", [doc.month, doc.fiscal_year]));
if(check){ frappe.confirm(__("Do you really want to Submit all Salary Slip for month {0} and year {1}", [doc.month, doc.fiscal_year]), function() {
// clear all in locals // clear all in locals
if(locals["Salary Slip"]) { if(locals["Salary Slip"]) {
$.each(locals["Salary Slip"], function(name, d) { $.each(locals["Salary Slip"], function(name, d) {
@ -40,7 +40,7 @@ cur_frm.cscript.submit_salary_slip = function(doc, cdt, cdn) {
} }
return $c('runserverobj', args={'method':'submit_salary_slip','docs':doc},callback); return $c('runserverobj', args={'method':'submit_salary_slip','docs':doc},callback);
} });
} }
cur_frm.cscript.make_bank_entry = function(doc,cdt,cdn){ cur_frm.cscript.make_bank_entry = function(doc,cdt,cdn){

View File

@ -4,11 +4,16 @@
frappe.query_reports["Employee Leave Balance"] = { frappe.query_reports["Employee Leave Balance"] = {
"filters": [ "filters": [
{ {
"fieldname":"fiscal_year", "fieldname":"from_date",
"label": __("Fiscal Year"), "label": __("From Date"),
"fieldtype": "Link", "fieldtype": "Date",
"options": "Fiscal Year", "default": frappe.datetime.year_start()
"default": frappe.defaults.get_user_default("fiscal_year") },
{
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.datetime.year_end()
}, },
{ {
"fieldname":"company", "fieldname":"company",

View File

@ -24,52 +24,47 @@ def execute(filters=None):
leave_types = frappe.db.sql_list("select name from `tabLeave Type`") leave_types = frappe.db.sql_list("select name from `tabLeave Type`")
if filters.get("fiscal_year"):
fiscal_years = [filters["fiscal_year"]]
else:
fiscal_years = frappe.db.sql_list("select name from `tabFiscal Year` order by name desc")
employee_names = [d.name for d in employees] employee_names = [d.name for d in employees]
allocations = frappe.db.sql("""select employee, fiscal_year, leave_type, total_leaves_allocated allocations = frappe.db.sql("""select employee, leave_type, sum(new_leaves_allocated) as leaves_allocated
from `tabLeave Allocation` from `tabLeave Allocation`
where docstatus=1 and employee in (%s)""" % where docstatus=1 and employee in (%s) and from_date >= '%s' and to_date <= '%s'""" %
','.join(['%s']*len(employee_names)), employee_names, as_dict=True) (','.join(['%s']*len(employee_names)), filters.get("from_date"),
filters.get("to_date")), employee_names, as_dict=True)
applications = frappe.db.sql("""select employee, fiscal_year, leave_type,
applications = frappe.db.sql("""select employee, leave_type,
SUM(total_leave_days) as leaves SUM(total_leave_days) as leaves
from `tabLeave Application` from `tabLeave Application`
where status="Approved" and docstatus = 1 and employee in (%s) where status="Approved" and docstatus = 1 and employee in (%s)
group by employee, fiscal_year, leave_type""" % and from_date >= '%s' and to_date <= '%s'
','.join(['%s']*len(employee_names)), employee_names, as_dict=True) group by employee, leave_type""" %
(','.join(['%s']*len(employee_names)), filters.get("from_date"),
filters.get("to_date")), employee_names, as_dict=True)
columns = [ columns = [
_("Fiscal Year"), _("Employee") + ":Link/Employee:150", _("Employee Name") + "::200", _("Department") +"::150" _("Employee") + ":Link/Employee:150", _("Employee Name") + "::200", _("Department") +"::150"
] ]
for leave_type in leave_types: for leave_type in leave_types:
columns.append(_(leave_type) + " " + _("Allocated") + ":Float") columns.append(_(leave_type) + " " + _("Opening") + ":Float")
columns.append(_(leave_type) + " " + _("Taken") + ":Float") columns.append(_(leave_type) + " " + _("Taken") + ":Float")
columns.append(_(leave_type) + " " + _("Balance") + ":Float") columns.append(_(leave_type) + " " + _("Balance") + ":Float")
data = {} data = {}
for d in allocations: for d in allocations:
data.setdefault((d.fiscal_year, d.employee, data.setdefault((d.employee,d.leave_type), frappe._dict()).allocation = d.leaves_allocated
d.leave_type), frappe._dict()).allocation = d.total_leaves_allocated
for d in applications: for d in applications:
data.setdefault((d.fiscal_year, d.employee, data.setdefault((d.employee, d.leave_type), frappe._dict()).leaves = d.leaves
d.leave_type), frappe._dict()).leaves = d.leaves
result = [] result = []
for fiscal_year in fiscal_years: for employee in employees:
for employee in employees: row = [employee.name, employee.employee_name, employee.department]
row = [fiscal_year, employee.name, employee.employee_name, employee.department] result.append(row)
result.append(row) for leave_type in leave_types:
for leave_type in leave_types: tmp = data.get((employee.name, leave_type), frappe._dict())
tmp = data.get((fiscal_year, employee.name, leave_type), frappe._dict()) row.append(tmp.allocation or 0)
row.append(tmp.allocation or 0) row.append(tmp.leaves or 0)
row.append(tmp.leaves or 0) row.append((tmp.allocation or 0) - (tmp.leaves or 0))
row.append((tmp.allocation or 0) - (tmp.leaves or 0))
return columns, result return columns, result

View File

@ -83,7 +83,7 @@ erpnext.production_order = {
frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Production Order'], frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Production Order'],
"icon-exclamation", "btn-default"); "icon-exclamation", "btn-default");
} else if (doc.status == 'Stopped') { } else if (doc.status == 'Stopped') {
frm.add_custom_button(__('Unstop'), cur_frm.cscript['Unstop Production Order'], frm.add_custom_button(__('Re-open'), cur_frm.cscript['Unstop Production Order'],
"icon-check", "btn-default"); "icon-check", "btn-default");
} }
@ -239,18 +239,11 @@ $.extend(cur_frm.cscript, {
}); });
cur_frm.cscript['Stop Production Order'] = function() { cur_frm.cscript['Stop Production Order'] = function() {
var doc = cur_frm.doc; $c_obj(cur_frm.doc, 'stop_unstop', 'Stopped', function(r, rt) {cur_frm.refresh();});
var check = confirm(__("Do you really want to stop production order: " + doc.name));
if (check) {
return $c_obj(doc, 'stop_unstop', 'Stopped', function(r, rt) {cur_frm.refresh();});
}
} }
cur_frm.cscript['Unstop Production Order'] = function() { cur_frm.cscript['Unstop Production Order'] = function() {
var doc = cur_frm.doc; $c_obj(cur_frm.doc, 'stop_unstop', 'Unstopped', function(r, rt) {cur_frm.refresh();});
var check = confirm(__("Do really want to unstop production order: " + doc.name));
if (check)
return $c_obj(doc, 'stop_unstop', 'Unstopped', function(r, rt) {cur_frm.refresh();});
} }
cur_frm.cscript['Transfer Raw Materials'] = function() { cur_frm.cscript['Transfer Raw Materials'] = function() {

View File

@ -9,7 +9,9 @@ def execute():
if outgoing and outgoing['mail_server'] and outgoing['mail_login']: if outgoing and outgoing['mail_server'] and outgoing['mail_login']:
account = frappe.new_doc("Email Account") account = frappe.new_doc("Email Account")
mapping = { mapping = {
"email_id": "mail_login", "login_id_is_different": 1,
"email_id": "auto_email_id",
"login_id": "mail_login",
"password": "mail_password", "password": "mail_password",
"footer": "footer", "footer": "footer",
"smtp_server": "mail_server", "smtp_server": "mail_server",

View File

@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import frappe import frappe
def execute(): def execute():
@ -11,9 +13,12 @@ def execute():
for m in frappe.get_all("Project Milestone", "*"): for m in frappe.get_all("Project Milestone", "*"):
if (m.milestone and m.milestone_date if (m.milestone and m.milestone_date
and frappe.db.exists("Project", m.parent)): and frappe.db.exists("Project", m.parent)):
subject = (m.milestone[:139] + "") if (len(m.milestone) > 140) else m.milestone
description = m.milestone
task = frappe.get_doc({ task = frappe.get_doc({
"doctype": "Task", "doctype": "Task",
"subject": m.milestone, "subject": subject,
"description": description if description!=subject else None,
"expected_start_date": m.milestone_date, "expected_start_date": m.milestone_date,
"status": "Open" if m.status=="Pending" else "Closed", "status": "Open" if m.status=="Pending" else "Closed",
"project": m.parent, "project": m.parent,

View File

@ -40,7 +40,17 @@ def fix_files_for_item(files_path, unlinked_files):
file_data = frappe.get_doc("File", unlinked_files[file_url]["file"]) file_data = frappe.get_doc("File", unlinked_files[file_url]["file"])
file_data.attached_to_doctype = "Item" file_data.attached_to_doctype = "Item"
file_data.attached_to_name = item_code file_data.attached_to_name = item_code
file_data.save() file_data.flags.ignore_folder_validate = True
try:
file_data.save()
except IOError:
print "File {0} does not exist".format(new_file_url)
# marking fix to prevent further errors
fixed_files.append(file_url)
continue
# set it as image in Item # set it as image in Item
if not frappe.db.get_value("Item", item_code, "image"): if not frappe.db.get_value("Item", item_code, "image"):

View File

@ -30,3 +30,6 @@ def execute():
frappe.reload_doctype("Sales Invoice") frappe.reload_doctype("Sales Invoice")
frappe.db.sql("""update `tabSales Invoice` set title = customer_name""") frappe.db.sql("""update `tabSales Invoice` set title = customer_name""")
frappe.reload_doctype("Expense Claim")
frappe.db.sql("""update `tabExpense Claim` set title = employee_name""")

View File

@ -0,0 +1,15 @@
from __future__ import unicode_literals
import frappe
def execute():
for leave_allocation in frappe.db.sql("select name, fiscal_year from `tabLeave Allocation`", as_dict=True):
year_start_date, year_end_date = frappe.db.get_value("Fiscal Year", leave_allocation["fiscal_year"],
["year_start_date", "year_end_date"])
frappe.db.sql("""update `tabLeave Allocation`
set from_date=%s, to_date=%s where name=%s""",
(year_start_date, year_end_date, leave_allocation["name"]))
frappe.db.commit()

View File

@ -28,7 +28,8 @@ class TimeLogBatch(Document):
d.update({ d.update({
"hours": tl.hours, "hours": tl.hours,
"activity_type": tl.activity_type, "activity_type": tl.activity_type,
"billing_amount": tl.billing_amount "billing_amount": tl.billing_amount,
"note": tl.note
}) })
def validate_time_log_is_submitted(self, tl): def validate_time_log_is_submitted(self, tl):

View File

@ -115,6 +115,27 @@
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "note",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Note",
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
} }
], ],
"hide_heading": 0, "hide_heading": 0,
@ -133,4 +154,4 @@
"permissions": [], "permissions": [],
"read_only": 0, "read_only": 0,
"read_only_onload": 0 "read_only_onload": 0
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 794 KiB

After

Width:  |  Height:  |  Size: 268 KiB

View File

@ -735,7 +735,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
var valid = true; var valid = true;
$.each(["company", "customer"], function(i, fieldname) { $.each(["company", "customer"], function(i, fieldname) {
if(frappe.meta.has_field(me.frm.doc.doctype, fieldname)) { if(frappe.meta.has_field(me.frm.doc.doctype, fieldname && me.frm.doc.doctype != "Purchase Order")) {
if (!me.frm.doc[fieldname]) { if (!me.frm.doc[fieldname]) {
msgprint(__("Please specify") + ": " + msgprint(__("Please specify") + ": " +
frappe.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + frappe.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) +

View File

@ -7,7 +7,7 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
method = "erpnext.accounts.party.get_party_details"; method = "erpnext.accounts.party.get_party_details";
} }
if(!args) { if(!args) {
if(frm.doc.customer) { if(frm.doctype != "Purchase Order" && frm.doc.customer) {
args = { args = {
party: frm.doc.customer, party: frm.doc.customer,
party_type: "Customer", party_type: "Customer",
@ -53,7 +53,7 @@ erpnext.utils.get_address_display = function(frm, address_field, display_field)
if(frm.updating_party_details) return; if(frm.updating_party_details) return;
if(!address_field) { if(!address_field) {
if(frm.doc.customer) { if(frm.doctype != "Purchase Order" && frm.doc.customer) {
address_field = "customer_address"; address_field = "customer_address";
} else if(frm.doc.supplier) { } else if(frm.doc.supplier) {
address_field = "supplier_address"; address_field = "supplier_address";

View File

@ -54,7 +54,7 @@ erpnext.selling.InstallationNote = frappe.ui.form.Controller.extend({
source_doctype: "Delivery Note", source_doctype: "Delivery Note",
get_query_filters: { get_query_filters: {
docstatus: 1, docstatus: 1,
status: ["!=", "Stopped"], status: ["not in", ["Stopped", "Closed"]],
per_installed: ["<", 99.99], per_installed: ["<", 99.99],
customer: cur_frm.doc.customer || undefined, customer: cur_frm.doc.customer || undefined,
company: cur_frm.doc.company company: cur_frm.doc.company

View File

@ -15,45 +15,66 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
refresh: function(doc, dt, dn) { refresh: function(doc, dt, dn) {
this._super(); this._super();
this.frm.dashboard.reset(); this.frm.dashboard.reset();
var is_delivered_by_supplier = false;
var is_delivery_note = false;
if(doc.docstatus==1) { if(doc.docstatus==1) {
if(doc.status != 'Stopped') { if(doc.status != 'Stopped' && doc.status != 'Closed') {
// cur_frm.dashboard.add_progress(cint(doc.per_delivered) + __("% Delivered"), $.each(cur_frm.doc.items, function(i, item){
// doc.per_delivered); if(item.delivered_by_supplier == 1 || item.supplier){
// cur_frm.dashboard.add_progress(cint(doc.per_billed) + __("% Billed"), if(item.qty > flt(item.ordered_qty))
// doc.per_billed); is_delivered_by_supplier = true;
}
else{
if(item.qty > flt(item.delivered_qty))
is_delivery_note = true;
}
})
// indent // material request
if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1) if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1
cur_frm.add_custom_button(__('Material Request'), this.make_material_request); && flt(doc.per_delivered, 2) < 100) {
cur_frm.add_custom_button(__('Material Request'), this.make_material_request);
}
// make purchase order
if(flt(doc.per_delivered, 2) < 100 && is_delivered_by_supplier) {
cur_frm.add_custom_button(__('Purchase Order'), cur_frm.cscript.make_purchase_order);
}
if(flt(doc.per_billed)==0) { if(flt(doc.per_billed)==0) {
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry); cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry);
} }
// stop // stop
if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100) if(flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed) < 100) {
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Sales Order']) cur_frm.add_custom_button(__('Stop'), this.stop_sales_order)
// maintenance
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
cur_frm.add_custom_button(__('Maint. Visit'), this.make_maintenance_visit);
cur_frm.add_custom_button(__('Maint. Schedule'), this.make_maintenance_schedule);
} }
// delivery note
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
cur_frm.add_custom_button(__('Delivery'), this.make_delivery_note).addClass("btn-primary");
// sales invoice cur_frm.add_custom_button(__('Close'), this.close_sales_order)
if(flt(doc.per_billed, 2) < 100) {
cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary"); // maintenance
} if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
cur_frm.add_custom_button(__('Maint. Visit'), this.make_maintenance_visit);
cur_frm.add_custom_button(__('Maint. Schedule'), this.make_maintenance_schedule);
}
// delivery note
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && is_delivery_note) {
cur_frm.add_custom_button(__('Delivery'), this.make_delivery_note).addClass("btn-primary");
}
// sales invoice
if(flt(doc.per_billed, 2) < 100) {
cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary");
}
} else { } else {
// un-stop // un-stop
cur_frm.add_custom_button(__('Unstop'), cur_frm.cscript['Unstop Sales Order']); cur_frm.add_custom_button(__('Re-open'), cur_frm.cscript['Unstop Sales Order']);
} }
} }
@ -136,15 +157,59 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
make_bank_entry: function() { make_bank_entry: function() {
return frappe.call({ return frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_from_sales_order", method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_order",
args: { args: {
"sales_order": cur_frm.doc.name "dt": "Sales Order",
"dn": cur_frm.doc.name
}, },
callback: function(r) { callback: function(r) {
var doclist = frappe.model.sync(r.message); var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name); frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
} }
}); });
},
make_purchase_order: function(){
var dialog = new frappe.ui.Dialog({
title: __("For Supplier"),
fields: [
{"fieldtype": "Link", "label": __("Supplier"), "fieldname": "supplier", "options":"Supplier",
"get_query": function () {
return {
query:"erpnext.selling.doctype.sales_order.sales_order.get_supplier",
filters: {'parent': cur_frm.doc.name}
}
}, "reqd": 1 },
{"fieldtype": "Button", "label": __("Make Purchase Order"), "fieldname": "make_purchase_order", "cssClass": "btn-primary"},
]
});
dialog.fields_dict.make_purchase_order.$input.click(function() {
args = dialog.get_values();
if(!args) return;
dialog.hide();
return frappe.call({
type: "GET",
method: "erpnext.selling.doctype.sales_order.sales_order.make_purchase_order_for_drop_shipment",
args: {
"source_name": cur_frm.doc.name,
"for_supplier": args.supplier
},
freeze: true,
callback: function(r) {
if(!r.exc) {
var doc = frappe.model.sync(r.message);
frappe.set_route("Form", r.message.doctype, r.message.name);
}
}
})
});
dialog.show();
},
stop_sales_order: function(){
cur_frm.cscript.update_status("Stop", "Stopped")
},
close_sales_order: function(){
cur_frm.cscript.update_status("Close", "Closed")
} }
}); });
@ -168,34 +233,23 @@ cur_frm.fields_dict['project_name'].get_query = function(doc, cdt, cdn) {
} }
} }
cur_frm.cscript['Stop Sales Order'] = function() { cur_frm.cscript.update_status = function(label, status){
var doc = cur_frm.doc; var doc = cur_frm.doc;
frappe.ui.form.is_saving = true;
var check = confirm(__("Are you sure you want to STOP ") + doc.name); frappe.call({
method: "erpnext.selling.doctype.sales_order.sales_order.update_status",
if (check) { args: {status: status, name: doc.name},
return $c('runserverobj', { callback: function(r){
'method':'stop_sales_order', cur_frm.reload_doc();
'docs': doc },
}, function(r,rt) { always: function() {
cur_frm.refresh(); frappe.ui.form.is_saving = false;
}); }
} });
} }
cur_frm.cscript['Unstop Sales Order'] = function() { cur_frm.cscript['Unstop Sales Order'] = function() {
var doc = cur_frm.doc; cur_frm.cscript.update_status('Re-open', 'Draft')
var check = confirm(__("Are you sure you want to UNSTOP ") + doc.name);
if (check) {
return $c('runserverobj', {
'method':'unstop_sales_order',
'docs': doc
}, function(r,rt) {
cur_frm.refresh();
});
}
} }
cur_frm.cscript.on_submit = function(doc, cdt, cdn) { cur_frm.cscript.on_submit = function(doc, cdt, cdn) {

View File

@ -86,7 +86,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Series", "label": "Series",
"no_copy": 1, "no_copy": 1,
"oldfieldname": "naming_series", "oldfieldname": "naming_series",
"oldfieldtype": "Select", "oldfieldtype": "Select",
@ -234,13 +234,14 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"default": "Sales", "default": "Sales",
"depends_on": "",
"fieldname": "order_type", "fieldname": "order_type",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Order Type", "label": "Order Type",
"no_copy": 0, "no_copy": 0,
"oldfieldname": "order_type", "oldfieldname": "order_type",
"oldfieldtype": "Select", "oldfieldtype": "Select",
@ -381,6 +382,7 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"depends_on": "",
"description": "", "description": "",
"fieldname": "po_no", "fieldname": "po_no",
"fieldtype": "Data", "fieldtype": "Data",
@ -1134,7 +1136,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Apply Additional Discount On", "label": "Apply Additional Discount On",
"no_copy": 0, "no_copy": 0,
"options": "\nGrand Total\nNet Total", "options": "\nGrand Total\nNet Total",
"permlevel": 0, "permlevel": 0,
@ -1808,7 +1810,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Source", "label": "Source",
"no_copy": 0, "no_copy": 0,
"oldfieldname": "source", "oldfieldname": "source",
"oldfieldtype": "Select", "oldfieldtype": "Select",
@ -1973,11 +1975,11 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 1, "in_filter": 1,
"in_list_view": 1, "in_list_view": 1,
"label": "Status", "label": "Status",
"no_copy": 1, "no_copy": 1,
"oldfieldname": "status", "oldfieldname": "status",
"oldfieldtype": "Select", "oldfieldtype": "Select",
"options": "\nDraft\nTo Deliver and Bill\nTo Bill\nTo Deliver\nCompleted\nStopped\nCancelled", "options": "\nDraft\nTo Deliver and Bill\nTo Bill\nTo Deliver\nCompleted\nStopped\nCancelled\nClosed",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1,
@ -1998,7 +2000,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Delivery Status", "label": "Delivery Status",
"no_copy": 1, "no_copy": 1,
"options": "Not Delivered\nFully Delivered\nPartly Delivered\nClosed\nNot Applicable", "options": "Not Delivered\nFully Delivered\nPartly Delivered\nClosed\nNot Applicable",
"permlevel": 0, "permlevel": 0,
@ -2092,7 +2094,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Billing Status", "label": "Billing Status",
"no_copy": 1, "no_copy": 1,
"options": "Not Billed\nFully Billed\nPartly Billed\nClosed", "options": "Not Billed\nFully Billed\nPartly Billed\nClosed",
"permlevel": 0, "permlevel": 0,
@ -2326,7 +2328,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Recurring Type", "label": "Recurring Type",
"no_copy": 1, "no_copy": 1,
"options": "\nMonthly\nQuarterly\nHalf-yearly\nYearly", "options": "\nMonthly\nQuarterly\nHalf-yearly\nYearly",
"permlevel": 0, "permlevel": 0,
@ -2553,7 +2555,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-10-03 07:39:10.525609", "modified": "2015-10-22 16:32:34.339835",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Order", "name": "Sales Order",

View File

@ -31,6 +31,7 @@ class SalesOrder(SellingController):
self.validate_uom_is_integer("stock_uom", "qty") self.validate_uom_is_integer("stock_uom", "qty")
self.validate_for_items() self.validate_for_items()
self.validate_warehouse() self.validate_warehouse()
self.validate_drop_ship()
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
make_packing_list(self) make_packing_list(self)
@ -67,7 +68,7 @@ class SalesOrder(SellingController):
if (frappe.db.get_value("Item", d.item_code, "is_stock_item")==1 or if (frappe.db.get_value("Item", d.item_code, "is_stock_item")==1 or
(self.has_product_bundle(d.item_code) and self.product_bundle_has_stock_item(d.item_code))) \ (self.has_product_bundle(d.item_code) and self.product_bundle_has_stock_item(d.item_code))) \
and not d.warehouse: and not d.warehouse and not cint(d.delivered_by_supplier):
frappe.throw(_("Delivery warehouse required for stock item {0}").format(d.item_code), frappe.throw(_("Delivery warehouse required for stock item {0}").format(d.item_code),
WarehouseRequired) WarehouseRequired)
@ -147,6 +148,11 @@ class SalesOrder(SellingController):
doc.set_status(update=True) doc.set_status(update=True)
doc.update_opportunity() doc.update_opportunity()
def validate_drop_ship(self):
for d in self.get('items'):
if d.delivered_by_supplier and not d.supplier:
frappe.throw(_("Row #{0}: Set Supplier for item {1}").format(d.idx, d.item_code))
def on_submit(self): def on_submit(self):
super(SalesOrder, self).on_submit() super(SalesOrder, self).on_submit()
@ -214,20 +220,13 @@ class SalesOrder(SellingController):
if date_diff and date_diff[0][0]: if date_diff and date_diff[0][0]:
frappe.throw(_("{0} {1} has been modified. Please refresh.").format(self.doctype, self.name)) frappe.throw(_("{0} {1} has been modified. Please refresh.").format(self.doctype, self.name))
def stop_sales_order(self): def update_status(self, status):
self.check_modified_date() self.check_modified_date()
self.db_set('status', 'Stopped') self.set_status(update=True, status=status)
self.update_reserved_qty() self.update_reserved_qty()
self.notify_update() self.notify_update()
clear_doctype_notifications(self) clear_doctype_notifications(self)
def unstop_sales_order(self):
self.check_modified_date()
self.db_set('status', 'Draft')
self.set_status(update=True)
self.update_reserved_qty()
clear_doctype_notifications(self)
def update_reserved_qty(self, so_item_rows=None): def update_reserved_qty(self, so_item_rows=None):
"""update requested qty (before ordered_qty is updated)""" """update requested qty (before ordered_qty is updated)"""
item_wh_list = [] item_wh_list = []
@ -253,6 +252,46 @@ class SalesOrder(SellingController):
def on_update(self): def on_update(self):
pass pass
def before_update_after_submit(self):
self.validate_drop_ship()
self.validate_supplier_after_submit()
def validate_supplier_after_submit(self):
"""Check that supplier is the same after submit if PO is already made"""
exc_list = []
for item in self.items:
if item.supplier:
supplier = frappe.db.get_value("Sales Order Item", {"parent": self.name, "item_code": item.item_code},
"supplier")
if item.ordered_qty > 0.0 and item.supplier != supplier:
exc_list.append(_("Row #{0}: Not allowed to change Supplier as Purchase Order already exists").format(item.idx))
if exc_list:
frappe.throw('\n'.join(exc_list))
def update_delivery_status(self, po_name):
"""Update delivery status from Purchase Order for drop shipping"""
tot_qty, delivered_qty = 0.0, 0.0
for item in self.items:
if item.delivered_by_supplier:
item_delivered_qty = frappe.db.sql("""select qty
from `tabPurchase Order Item` poi, `tabPurchase Order` po
where poi.prevdoc_docname = %s
and poi.prevdoc_doctype = 'Sales Order'
and poi.item_code = %s
and poi.parent = po.name
and po.status = 'Delivered'""", (self.name, item.item_code))
item_delivered_qty = item_delivered_qty[0][0] if item_delivered_qty else 0
item.db_set("delivered_qty", item_delivered_qty)
delivered_qty += item.delivered_qty
tot_qty += item.qty
frappe.db.set_value("Sales Order", self.name, "per_delivered", flt(delivered_qty/tot_qty) * 100)
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)
@ -268,12 +307,12 @@ def stop_or_unstop_sales_orders(names, status):
for name in names: for name in names:
so = frappe.get_doc("Sales Order", name) so = frappe.get_doc("Sales Order", name)
if so.docstatus == 1: if so.docstatus == 1:
if status=="Stop": if status in ("Stopped", "Closed"):
if so.status not in ("Stopped", "Cancelled") and (so.per_delivered < 100 or so.per_billed < 100): if so.status not in ("Stopped", "Cancelled", "Closed") and (so.per_delivered < 100 or so.per_billed < 100):
so.stop_sales_order() so.update_status(status)
else: else:
if so.status == "Stopped": if so.status in ("Stopped", "Closed"):
so.unstop_sales_order() so.update_status('Draft')
frappe.local.message_log = [] frappe.local.message_log = []
@ -350,7 +389,7 @@ def make_delivery_note(source_name, target_doc=None):
"parent": "against_sales_order", "parent": "against_sales_order",
}, },
"postprocess": update_item, "postprocess": update_item,
"condition": lambda doc: doc.delivered_qty < doc.qty "condition": lambda doc: doc.delivered_qty < doc.qty and doc.delivered_by_supplier!=1
}, },
"Sales Taxes and Charges": { "Sales Taxes and Charges": {
"doctype": "Sales Taxes and Charges", "doctype": "Sales Taxes and Charges",
@ -488,3 +527,96 @@ def get_events(start, end, filters=None):
"end": end "end": end
}, as_dict=True, update={"allDay": 0}) }, as_dict=True, update={"allDay": 0})
return data return data
@frappe.whitelist()
def make_purchase_order_for_drop_shipment(source_name, for_supplier, target_doc=None):
def set_missing_values(source, target):
target.supplier = for_supplier
default_price_list = frappe.get_value("Supplier", for_supplier, "default_price_list")
if default_price_list:
target.buying_price_list = default_price_list
target.delivered_by_supplier = 1
target.run_method("set_missing_values")
target.run_method("calculate_taxes_and_totals")
def update_item(source, target, source_parent):
target.schedule_date = source_parent.delivery_date
target.qty = flt(source.qty) - flt(source.ordered_qty)
doclist = get_mapped_doc("Sales Order", source_name, {
"Sales Order": {
"doctype": "Purchase Order",
"field_map": {
"customer_address": "customer_address",
"contact_person": "customer_contact_person",
"address_display": "customer_address_display",
"contact_display": "customer_contact_display",
"contact_mobile": "customer_contact_mobile",
"contact_email": "customer_contact_email",
},
"field_no_map": [
"address_display",
"contact_display",
"contact_mobile",
"contact_email",
"contact_person"
],
"validation": {
"docstatus": ["=", 1]
}
},
"Sales Order Item": {
"doctype": "Purchase Order Item",
"field_map": [
["name", "prevdoc_detail_docname"],
["parent", "prevdoc_docname"],
["parenttype", "prevdoc_doctype"],
["uom", "stock_uom"],
["delivery_date", "schedule_date"]
],
"field_no_map": [
"rate",
"price_list_rate"
],
"postprocess": update_item,
"condition": lambda doc: doc.ordered_qty < doc.qty and doc.supplier == for_supplier
}
}, target_doc, set_missing_values)
return doclist
@frappe.whitelist()
def get_supplier(doctype, txt, searchfield, start, page_len, filters):
supp_master_name = frappe.defaults.get_user_default("supp_master_name")
if supp_master_name == "Supplier Name":
fields = ["name", "supplier_type"]
else:
fields = ["name", "supplier_name", "supplier_type"]
fields = ", ".join(fields)
return frappe.db.sql("""select {field} from `tabSupplier`
where docstatus < 2
and ({key} like %(txt)s
or supplier_name like %(txt)s)
and name in (select supplier from `tabSales Order Item` where parent = %(parent)s)
order by
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
if(locate(%(_txt)s, supplier_name), locate(%(_txt)s, supplier_name), 99999),
name, supplier_name
limit %(start)s, %(page_len)s """.format(**{
'field': fields,
'key': searchfield
}), {
'txt': "%%%s%%" % txt,
'_txt': txt.replace("%", ""),
'start': start,
'page_len': page_len,
'parent': filters.get('parent')
})
@frappe.whitelist()
def update_status(status, name):
so = frappe.get_doc("Sales Order", name)
so.update_status(status)

View File

@ -5,6 +5,9 @@ frappe.listview_settings['Sales Order'] = {
if(doc.status==="Stopped") { if(doc.status==="Stopped") {
return [__("Stopped"), "darkgrey", "status,=,Stopped"]; return [__("Stopped"), "darkgrey", "status,=,Stopped"];
} else if(doc.status==="Closed"){
return [__("Closed"), "green", "status,=,Closed"];
} else if (doc.order_type !== "Maintenance" } else if (doc.order_type !== "Maintenance"
&& flt(doc.per_delivered, 2) < 100 && frappe.datetime.get_diff(doc.delivery_date) < 0) { && flt(doc.per_delivered, 2) < 100 && frappe.datetime.get_diff(doc.delivery_date) < 0) {
// to bill & overdue // to bill & overdue
@ -41,11 +44,15 @@ frappe.listview_settings['Sales Order'] = {
onload: function(listview) { onload: function(listview) {
var method = "erpnext.selling.doctype.sales_order.sales_order.stop_or_unstop_sales_orders"; var method = "erpnext.selling.doctype.sales_order.sales_order.stop_or_unstop_sales_orders";
listview.page.add_menu_item(__("Set as Stopped"), function() { listview.page.add_menu_item(__("Close"), function() {
listview.call_for_selected_items(method, {"status": "Stop"}); listview.call_for_selected_items(method, {"status": "Closed"});
}); });
listview.page.add_menu_item(__("Set as Unstopped"), function() { listview.page.add_menu_item(__("Stop"), function() {
listview.call_for_selected_items(method, {"status": "Stoped"});
});
listview.page.add_menu_item(__("Re-open"), function() {
listview.call_for_selected_items(method, {"status": "Unstop"}); listview.call_for_selected_items(method, {"status": "Unstop"});
}); });

View File

@ -7,8 +7,6 @@ import frappe.permissions
import unittest import unittest
from erpnext.selling.doctype.sales_order.sales_order \ from erpnext.selling.doctype.sales_order.sales_order \
import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired
from erpnext.accounts.doctype.journal_entry.test_journal_entry \
import make_journal_entry
from frappe.tests.test_permissions import set_user_permission_doctypes from frappe.tests.test_permissions import set_user_permission_doctypes
@ -97,12 +95,12 @@ class TestSalesOrder(unittest.TestCase):
# stop so # stop so
so.load_from_db() so.load_from_db()
so.stop_sales_order() so.update_status("Stopped")
self.assertEqual(get_reserved_qty(), existing_reserved_qty) self.assertEqual(get_reserved_qty(), existing_reserved_qty)
# unstop so # unstop so
so.load_from_db() so.load_from_db()
so.unstop_sales_order() so.update_status('Draft')
self.assertEqual(get_reserved_qty(), existing_reserved_qty + 5) self.assertEqual(get_reserved_qty(), existing_reserved_qty + 5)
dn.cancel() dn.cancel()
@ -147,14 +145,14 @@ class TestSalesOrder(unittest.TestCase):
# stop so # stop so
so.load_from_db() so.load_from_db()
so.stop_sales_order() so.update_status("Stopped")
self.assertEqual(get_reserved_qty("_Test Item"), existing_reserved_qty_item1) self.assertEqual(get_reserved_qty("_Test Item"), existing_reserved_qty_item1)
self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"), existing_reserved_qty_item2) self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"), existing_reserved_qty_item2)
# unstop so # unstop so
so.load_from_db() so.load_from_db()
so.unstop_sales_order() so.update_status('Draft')
self.assertEqual(get_reserved_qty("_Test Item"), existing_reserved_qty_item1 + 25) self.assertEqual(get_reserved_qty("_Test Item"), existing_reserved_qty_item1 + 25)
self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"), self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"),
@ -296,6 +294,122 @@ class TestSalesOrder(unittest.TestCase):
frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1) frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1)
def test_drop_shipping(self):
from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order_for_drop_shipment
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.buying.doctype.purchase_order.purchase_order import update_status
po_item = make_item("_Test Item for Drop Shipping", {"is_stock_item": 1, "is_sales_item": 1,
"is_purchase_item": 1, "delivered_by_supplier": 1, 'default_supplier': '_Test Supplier',
"expense_account": "_Test Account Cost for Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC"
})
dn_item = make_item("_Test Regular Item", {"is_stock_item": 1, "is_sales_item": 1,
"is_purchase_item": 1, "expense_account": "_Test Account Cost for Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC"})
so_items = [
{
"item_code": po_item.item_code,
"warehouse": "",
"qty": 2,
"rate": 400,
"delivered_by_supplier": 1,
"supplier": '_Test Supplier'
},
{
"item_code": dn_item.item_code,
"warehouse": "_Test Warehouse - _TC",
"qty": 2,
"rate": 300,
"conversion_factor": 1.0
}
]
#setuo existing qty from bin
bin = frappe.get_all("Bin", filters={"item_code": po_item.item_code, "warehouse": "_Test Warehouse - _TC"},
fields=["ordered_qty", "reserved_qty"])
existing_ordered_qty = bin[0].ordered_qty if bin else 0.0
existing_reserved_qty = bin[0].reserved_qty if bin else 0.0
bin = frappe.get_all("Bin", filters={"item_code": dn_item.item_code, "warehouse": "_Test Warehouse - _TC"},
fields=["reserved_qty"])
existing_reserved_qty_for_dn_item = bin[0].reserved_qty if bin else 0.0
#create so, po and partial dn
so = make_sales_order(item_list=so_items, do_not_submit=True)
so.submit()
po = make_purchase_order_for_drop_shipment(so.name, '_Test Supplier')
po.submit()
dn = create_dn_against_so(so.name, delivered_qty=1)
self.assertEquals(so.customer, po.customer)
self.assertEquals(po.items[0].prevdoc_doctype, "Sales Order")
self.assertEquals(po.items[0].prevdoc_docname, so.name)
self.assertEquals(po.items[0].item_code, po_item.item_code)
self.assertEquals(dn.items[0].item_code, dn_item.item_code)
#test ordered_qty and reserved_qty
ordered_qty, reserved_qty = frappe.db.get_value("Bin",
{"item_code": po_item.item_code, "warehouse": "_Test Warehouse - _TC"}, ["ordered_qty", "reserved_qty"])
self.assertEquals(abs(flt(ordered_qty)), existing_ordered_qty + so_items[0]['qty'])
self.assertEquals(abs(flt(reserved_qty)), existing_reserved_qty)
reserved_qty = frappe.db.get_value("Bin",
{"item_code": dn_item.item_code, "warehouse": "_Test Warehouse - _TC"}, "reserved_qty")
self.assertEquals(abs(flt(reserved_qty)), existing_reserved_qty_for_dn_item + 1)
#test po_item length
self.assertEquals(len(po.items), 1)
#test per_delivered status
update_status("Delivered", po.name)
self.assertEquals(flt(frappe.db.get_value("Sales Order", so.name, "per_delivered"), 2), 75.00)
#test reserved qty after complete delivery
dn = create_dn_against_so(so.name, delivered_qty=1)
reserved_qty = frappe.db.get_value("Bin",
{"item_code": dn_item.item_code, "warehouse": "_Test Warehouse - _TC"}, "reserved_qty")
self.assertEquals(abs(flt(reserved_qty)), existing_reserved_qty_for_dn_item)
#test after closing so
so.db_set('status', "Closed")
so.update_reserved_qty()
ordered_qty, reserved_qty = frappe.db.get_value("Bin",
{"item_code": po_item.item_code, "warehouse": "_Test Warehouse - _TC"}, ["ordered_qty", "reserved_qty"])
self.assertEquals(abs(flt(ordered_qty)), existing_ordered_qty)
self.assertEquals(abs(flt(reserved_qty)), existing_reserved_qty)
reserved_qty = frappe.db.get_value("Bin",
{"item_code": dn_item.item_code, "warehouse": "_Test Warehouse - _TC"}, "reserved_qty")
self.assertEquals(abs(flt(reserved_qty)), existing_reserved_qty)
def test_reserved_qty_for_closing_so(self):
bin = frappe.get_all("Bin", filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
fields=["reserved_qty"])
existing_reserved_qty = bin[0].reserved_qty if bin else 0.0
so = make_sales_order(item_code="_Test Item", qty=1)
self.assertEquals(get_reserved_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"), existing_reserved_qty+1)
so.update_status("Closed")
self.assertEquals(get_reserved_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"), existing_reserved_qty)
def make_sales_order(**args): def make_sales_order(**args):
so = frappe.new_doc("Sales Order") so = frappe.new_doc("Sales Order")
args = frappe._dict(args) args = frappe._dict(args)
@ -312,13 +426,18 @@ def make_sales_order(**args):
if "warehouse" not in args: if "warehouse" not in args:
args.warehouse = "_Test Warehouse - _TC" args.warehouse = "_Test Warehouse - _TC"
so.append("items", { if args.item_list:
"item_code": args.item or args.item_code or "_Test Item", for item in args.item_list:
"warehouse": args.warehouse, so.append("items", item)
"qty": args.qty or 10,
"rate": args.rate or 100, else:
"conversion_factor": 1.0, so.append("items", {
}) "item_code": args.item or args.item_code or "_Test Item",
"warehouse": args.warehouse,
"qty": args.qty or 10,
"rate": args.rate or 100,
"conversion_factor": 1.0,
})
if not args.do_not_save: if not args.do_not_save:
so.insert() so.insert()

File diff suppressed because it is too large Load Diff

View File

@ -111,7 +111,8 @@ class EmailDigest(Document):
"""Set standard digest style""" """Set standard digest style"""
context.text_muted = '#8D99A6' context.text_muted = '#8D99A6'
context.text_color = '#36414C' context.text_color = '#36414C'
context.h1 = 'margin-bottom: 30px; margin-bottom: 0; margin-top: 40px; font-weight: 400;' context.h1 = 'margin-bottom: 30px; margin-top: 40px; font-weight: 400; font-size: 30px;'
context.h2 = 'margin-bottom: 30px; margin-top: -20px; font-weight: 400; font-size: 20px;'
context.label_css = '''display: inline-block; color: {text_muted}; context.label_css = '''display: inline-block; color: {text_muted};
padding: 3px 7px; margin-right: 7px;'''.format(text_muted = context.text_muted) padding: 3px 7px; margin-right: 7px;'''.format(text_muted = context.text_muted)
context.section_head = 'margin-top: 60px; font-size: 16px;' context.section_head = 'margin-top: 60px; font-size: 16px;'
@ -152,7 +153,7 @@ class EmailDigest(Document):
todo_list = frappe.db.sql("""select * todo_list = frappe.db.sql("""select *
from `tabToDo` where (owner=%s or assigned_by=%s) and status="Open" from `tabToDo` where (owner=%s or assigned_by=%s) and status="Open"
order by field(priority, 'High', 'Medium', 'Low') asc, date asc""", order by field(priority, 'High', 'Medium', 'Low') asc, date asc limit 20""",
(user_id, user_id), as_dict=True) (user_id, user_id), as_dict=True)
for t in todo_list: for t in todo_list:
@ -289,6 +290,7 @@ class EmailDigest(Document):
elif self.frequency == "Weekly": elif self.frequency == "Weekly":
# from date is the previous week's monday # from date is the previous week's monday
from_date = today - timedelta(days=today.weekday(), weeks=1) from_date = today - timedelta(days=today.weekday(), weeks=1)
# to date is sunday i.e. the previous day # to date is sunday i.e. the previous day
to_date = from_date + timedelta(days=6) to_date = from_date + timedelta(days=6)
else: else:
@ -300,32 +302,18 @@ class EmailDigest(Document):
return from_date, to_date return from_date, to_date
def set_dates(self): def set_dates(self):
today = now_datetime().date() self.future_from_date, self.future_to_date = self.from_date, self.to_date
# decide from date based on email digest frequency # decide from date based on email digest frequency
if self.frequency == "Daily": if self.frequency == "Daily":
# from date, to_date is today self.past_from_date = self.past_to_date = self.future_from_date - relativedelta(days = 1)
self.future_from_date = self.future_to_date = today
self.past_from_date = self.past_to_date = today - relativedelta(days = 1)
elif self.frequency == "Weekly": elif self.frequency == "Weekly":
# from date is the current week's monday self.past_from_date = self.future_from_date - relativedelta(weeks=1)
self.future_from_date = today - relativedelta(days=today.weekday()) self.past_to_date = self.future_from_date - relativedelta(days=1)
# to date is the current week's sunday
self.future_to_date = self.future_from_date + relativedelta(days=6)
self.past_from_date = self.future_from_date - relativedelta(days=7)
self.past_to_date = self.future_to_date - relativedelta(days=7)
else: else:
# from date is the 1st day of the current month self.past_from_date = self.future_from_date - relativedelta(months=1)
self.future_from_date = today - relativedelta(days=today.day-1) self.past_to_date = self.future_from_date - relativedelta(days=1)
# to date is the last day of the current month
self.future_to_date = self.future_from_date + relativedelta(days=-1, months=1)
self.past_from_date = self.future_from_date - relativedelta(month=1)
self.past_to_date = self.future_to_date - relativedelta(month=1)
def get_next_sending(self): def get_next_sending(self):
from_date, to_date = self.get_from_to_date() from_date, to_date = self.get_from_to_date()

View File

@ -11,6 +11,7 @@
<div style="max-width: 500px; margin: auto; padding: 20px 0 40px 0"> <div style="max-width: 500px; margin: auto; padding: 20px 0 40px 0">
<h1 style="{{ h1 }}">{{ title }}</h1> <h1 style="{{ h1 }}">{{ title }}</h1>
<h2 style="{{ h2 }}">{{ company }}</h2>
<h4 style="font-weight: normal; color: {{ text_muted }}; margin-top: 7px; font-size: 16px; margin-top: 7px;"> <h4 style="font-weight: normal; color: {{ text_muted }}; margin-top: 7px; font-size: 16px; margin-top: 7px;">
<p>{% if frequency == "Daily" %} <p>{% if frequency == "Daily" %}
{{ frappe.format_date(future_from_date) }} {{ frappe.format_date(future_from_date) }}

View File

@ -4,7 +4,17 @@ frappe.pages['welcome-to-erpnext'].on_page_load = function(wrapper) {
parent.html(frappe.render_template("welcome_to_erpnext", {})); parent.html(frappe.render_template("welcome_to_erpnext", {}));
parent.find(".video-placeholder").on("click", function() { parent.find(".video-placeholder").on("click", function() {
window.erpnext_welcome_video_started = true;
parent.find(".video-placeholder").addClass("hidden"); parent.find(".video-placeholder").addClass("hidden");
parent.find(".embed-responsive").append('<iframe class="embed-responsive-item video-playlist" src="https://www.youtube.com/embed/videoseries?list=PL3lFfCEoMxvxDHtYyQFJeUYkWzQpXwFM9&color=white&autoplay=1" allowfullscreen></iframe>') parent.find(".embed-responsive").append('<iframe class="embed-responsive-item video-playlist" src="https://www.youtube.com/embed/videoseries?list=PL3lFfCEoMxvxDHtYyQFJeUYkWzQpXwFM9&color=white&autoplay=1&enablejsapi=1" allowfullscreen></iframe>')
});
// pause video on page change
$(document).on("page-change", function() {
if (window.erpnext_welcome_video_started && parent) {
parent.find(".video-playlist").each(function() {
this.contentWindow.postMessage('{"event":"command","func":"' + 'pauseVideo' + '","args":""}', '*');
});
}
}); });
} }

View File

@ -9,7 +9,7 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
refresh: function(doc, dt, dn) { refresh: function(doc, dt, dn) {
this._super(); this._super();
if (!doc.is_return) { if (!doc.is_return && doc.status!="Closed") {
if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1) if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1)
cur_frm.add_custom_button(__('Installation Note'), this.make_installation_note); cur_frm.add_custom_button(__('Installation Note'), this.make_installation_note);
@ -30,7 +30,7 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
source_doctype: "Sales Order", source_doctype: "Sales Order",
get_query_filters: { get_query_filters: {
docstatus: 1, docstatus: 1,
status: ["!=", "Stopped"], status: ["not in", ["Stopped", "Closed"]],
per_delivered: ["<", 99.99], per_delivered: ["<", 99.99],
project_name: cur_frm.doc.project_name || undefined, project_name: cur_frm.doc.project_name || undefined,
customer: cur_frm.doc.customer || undefined, customer: cur_frm.doc.customer || undefined,
@ -46,9 +46,13 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
if (cint(frappe.defaults.get_default("auto_accounting_for_stock"))) { if (cint(frappe.defaults.get_default("auto_accounting_for_stock"))) {
this.show_general_ledger(); this.show_general_ledger();
} }
if(doc.status !== "Closed") {
cur_frm.add_custom_button(__("Close"), this.close_delivery_note)
}
} }
if(doc.__onload && !doc.__onload.billing_complete && doc.docstatus==1 && !doc.is_return) { if(doc.__onload && !doc.__onload.billing_complete && doc.docstatus==1 && !doc.is_return && doc.status!="Closed") {
// show Make Invoice button only if Delivery Note is not created from Sales Invoice // show Make Invoice button only if Delivery Note is not created from Sales Invoice
var from_sales_invoice = false; var from_sales_invoice = false;
from_sales_invoice = cur_frm.doc.items.some(function(item) { from_sales_invoice = cur_frm.doc.items.some(function(item) {
@ -59,6 +63,9 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary"); cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary");
} }
if(doc.docstatus==1 && doc.status === "Closed") {
cur_frm.add_custom_button(__('Re-open'), this.reopen_delivery_note)
}
erpnext.stock.delivery_note.set_print_hide(doc, dt, dn); erpnext.stock.delivery_note.set_print_hide(doc, dt, dn);
// unhide expense_account and cost_center is auto_accounting_for_stock enabled // unhide expense_account and cost_center is auto_accounting_for_stock enabled
@ -93,6 +100,14 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
items_on_form_rendered: function(doc, grid_row) { items_on_form_rendered: function(doc, grid_row) {
erpnext.setup_serial_no(); erpnext.setup_serial_no();
},
close_delivery_note: function(doc){
cur_frm.cscript.update_status("Closed")
},
reopen_delivery_note : function() {
cur_frm.cscript.update_status("Submitted")
} }
}); });
@ -108,6 +123,21 @@ cur_frm.cscript.new_contact = function(){
} }
cur_frm.cscript.update_status = function(status) {
frappe.ui.form.is_saving = true;
frappe.call({
method:"erpnext.stock.doctype.delivery_note.delivery_note.update_delivery_note_status",
args: {docname: cur_frm.doc.name, status: status},
callback: function(r){
if(!r.exc)
cur_frm.reload_doc();
},
always: function(){
frappe.ui.form.is_saving = false;
}
})
}
// ***************** Get project name ***************** // ***************** Get project name *****************
cur_frm.fields_dict['project_name'].get_query = function(doc, cdt, cdn) { cur_frm.fields_dict['project_name'].get_query = function(doc, cdt, cdn) {
return { return {

View File

@ -87,7 +87,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Series", "label": "Series",
"no_copy": 1, "no_copy": 1,
"oldfieldname": "naming_series", "oldfieldname": "naming_series",
"oldfieldtype": "Select", "oldfieldtype": "Select",
@ -1205,7 +1205,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Apply Additional Discount On", "label": "Apply Additional Discount On",
"no_copy": 0, "no_copy": 0,
"options": "\nGrand Total\nNet Total", "options": "\nGrand Total\nNet Total",
"permlevel": 0, "permlevel": 0,
@ -1873,7 +1873,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Source", "label": "Source",
"no_copy": 0, "no_copy": 0,
"oldfieldname": "source", "oldfieldname": "source",
"oldfieldtype": "Select", "oldfieldtype": "Select",
@ -2108,11 +2108,11 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 1, "in_filter": 1,
"in_list_view": 0, "in_list_view": 0,
"label": "Status", "label": "Status",
"no_copy": 1, "no_copy": 1,
"oldfieldname": "status", "oldfieldname": "status",
"oldfieldtype": "Select", "oldfieldtype": "Select",
"options": "\nDraft\nSubmitted\nCancelled", "options": "\nDraft\nSubmitted\nCancelled\nClosed",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"print_width": "150px", "print_width": "150px",
@ -2438,7 +2438,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-10-02 07:38:44.497411", "modified": "2015-11-02 01:20:58.934509",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Delivery Note", "name": "Delivery Note",

View File

@ -10,6 +10,8 @@ from frappe import msgprint, _
import frappe.defaults import frappe.defaults
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.selling_controller import SellingController from erpnext.controllers.selling_controller import SellingController
from frappe.desk.notifications import clear_doctype_notifications
form_grid_templates = { form_grid_templates = {
"items": "templates/form_grid/item_grid.html" "items": "templates/form_grid/item_grid.html"
@ -106,7 +108,7 @@ class DeliveryNote(SellingController):
self.set_status() self.set_status()
self.so_required() self.so_required()
self.validate_proj_cust() self.validate_proj_cust()
self.check_stop_sales_order("against_sales_order") self.check_stop_or_close_sales_order("against_sales_order")
self.validate_for_items() self.validate_for_items()
self.validate_warehouse() self.validate_warehouse()
self.validate_uom_is_integer("stock_uom", "qty") self.validate_uom_is_integer("stock_uom", "qty")
@ -203,7 +205,7 @@ class DeliveryNote(SellingController):
def on_cancel(self): def on_cancel(self):
self.check_stop_sales_order("against_sales_order") self.check_stop_or_close_sales_order("against_sales_order")
self.check_next_docstatus() self.check_next_docstatus()
self.update_prevdoc_status() self.update_prevdoc_status()
@ -214,10 +216,10 @@ class DeliveryNote(SellingController):
self.cancel_packing_slips() self.cancel_packing_slips()
self.make_gl_entries_on_cancel() self.make_gl_entries_on_cancel()
def check_credit_limit(self): def check_credit_limit(self):
from erpnext.selling.doctype.customer.customer import check_credit_limit from erpnext.selling.doctype.customer.customer import check_credit_limit
validate_against_credit_limit = False validate_against_credit_limit = False
for d in self.get("items"): for d in self.get("items"):
if not (d.against_sales_order or d.against_sales_invoice): if not (d.against_sales_order or d.against_sales_invoice):
@ -268,6 +270,11 @@ class DeliveryNote(SellingController):
ps.cancel() ps.cancel()
frappe.msgprint(_("Packing Slip(s) cancelled")) frappe.msgprint(_("Packing Slip(s) cancelled"))
def update_status(self, status):
self.set_status(update=True, status=status)
self.notify_update()
clear_doctype_notifications(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)
@ -386,3 +393,9 @@ def make_packing_slip(source_name, target_doc=None):
def make_sales_return(source_name, target_doc=None): def make_sales_return(source_name, target_doc=None):
from erpnext.controllers.sales_and_purchase_return import make_return_doc from erpnext.controllers.sales_and_purchase_return import make_return_doc
return make_return_doc("Delivery Note", source_name, target_doc) return make_return_doc("Delivery Note", source_name, target_doc)
@frappe.whitelist()
def update_delivery_note_status(docname, status):
dn = frappe.get_doc("Delivery Note", docname)
dn.update_status(status)

View File

@ -1,9 +1,11 @@
frappe.listview_settings['Delivery Note'] = { frappe.listview_settings['Delivery Note'] = {
add_fields: ["customer", "customer_name", "base_grand_total", "per_installed", add_fields: ["customer", "customer_name", "base_grand_total", "per_installed",
"transporter_name", "grand_total", "is_return"], "transporter_name", "grand_total", "is_return", "status"],
get_indicator: function(doc) { get_indicator: function(doc) {
if(cint(doc.is_return)==1) { if(cint(doc.is_return)==1) {
return [__("Return"), "darkgrey", "is_return,=,Yes"]; return [__("Return"), "darkgrey", "is_return,=,Yes"];
} } else if(doc.status==="Closed") {
return [__("Closed"), "green", "status,=,Closed"];
}
} }
}; };

View File

@ -396,6 +396,15 @@ class TestDeliveryNote(unittest.TestCase):
self.assertEquals([gle.debit, gle.credit], expected_values.get(gle.account)) self.assertEquals([gle.debit, gle.credit], expected_values.get(gle.account))
set_perpetual_inventory(0) set_perpetual_inventory(0)
def test_closed_delivery_note(self):
from erpnext.stock.doctype.delivery_note.delivery_note import update_delivery_note_status
dn = create_delivery_note(do_not_submit=True)
dn.submit()
update_delivery_note_status(dn.name, "Closed")
self.assertEquals(frappe.db.get_value("Delivery Note", dn.name, "Status"), "Closed")
def create_delivery_note(**args): def create_delivery_note(**args):
dn = frappe.new_doc("Delivery Note") dn = frappe.new_doc("Delivery Note")

View File

@ -1188,6 +1188,28 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "delivered_by_supplier",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Delivered by Supplier (Drop Ship)",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 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,
@ -2135,7 +2157,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 1, "max_attachments": 1,
"modified": "2015-10-29 02:25:26.256373", "modified": "2015-11-04 04:50:02.051468",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Item", "name": "Item",

View File

@ -28,14 +28,6 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten
refresh: function(doc) { refresh: function(doc) {
this._super(); this._super();
// dashboard
cur_frm.dashboard.reset();
if(doc.docstatus===1) {
if(doc.status==="Stopped") {
cur_frm.dashboard.set_headline_alert(__("Stopped"), "alert-danger", "octicon octicon-circle-slash")
}
}
if(doc.docstatus==0) { if(doc.docstatus==0) {
cur_frm.add_custom_button(__("Get Items from BOM"), cur_frm.add_custom_button(__("Get Items from BOM"),
cur_frm.cscript.get_items_from_bom, "icon-sitemap", "btn-default"); cur_frm.cscript.get_items_from_bom, "icon-sitemap", "btn-default");
@ -84,7 +76,7 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten
} }
if(doc.docstatus == 1 && doc.status == 'Stopped') if(doc.docstatus == 1 && doc.status == 'Stopped')
cur_frm.add_custom_button(__('Unstop Material Request'), cur_frm.add_custom_button(__('Re-open'),
cur_frm.cscript['Unstop Material Request'], "icon-check"); cur_frm.cscript['Unstop Material Request'], "icon-check");
}, },
@ -175,24 +167,16 @@ $.extend(cur_frm.cscript, new erpnext.buying.MaterialRequestController({frm: cur
cur_frm.cscript['Stop Material Request'] = function() { cur_frm.cscript['Stop Material Request'] = function() {
var doc = cur_frm.doc; var doc = cur_frm.doc;
var check = confirm(__("Do you really want to STOP this Material Request?")); $c('runserverobj', args={'method':'update_status', 'arg': 'Stopped', 'docs': doc}, function(r,rt) {
cur_frm.refresh();
if (check) { });
return $c('runserverobj', args={'method':'update_status', 'arg': 'Stopped', 'docs': doc}, function(r,rt) {
cur_frm.refresh();
});
}
}; };
cur_frm.cscript['Unstop Material Request'] = function(){ cur_frm.cscript['Unstop Material Request'] = function(){
var doc = cur_frm.doc; var doc = cur_frm.doc;
var check = confirm(__("Do you really want to UNSTOP this Material Request?")); $c('runserverobj', args={'method':'update_status', 'arg': 'Submitted','docs': doc}, function(r,rt) {
cur_frm.refresh();
if (check) { });
return $c('runserverobj', args={'method':'update_status', 'arg': 'Submitted','docs': doc}, function(r,rt) {
cur_frm.refresh();
});
}
}; };

View File

@ -98,12 +98,11 @@ class MaterialRequest(BuyingController):
self.check_modified_date() self.check_modified_date()
frappe.db.set(self, 'status', cstr(status)) frappe.db.set(self, 'status', cstr(status))
self.update_requested_qty() self.update_requested_qty()
frappe.msgprint(_("Status updated to {0}").format(_(status)))
def on_cancel(self): def on_cancel(self):
pc_obj = frappe.get_doc('Purchase Common') pc_obj = frappe.get_doc('Purchase Common')
pc_obj.check_for_stopped_status(self.doctype, self.name) pc_obj.check_for_stopped_or_closed_status(self.doctype, self.name)
pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Order', docname = self.name, detail_doctype = 'Purchase Order Item') pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Order', docname = self.name, detail_doctype = 'Purchase Order Item')
self.update_requested_qty() self.update_requested_qty()

View File

@ -29,11 +29,14 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
refresh: function() { refresh: function() {
this._super(); this._super();
this.show_stock_ledger(); if(this.frm.doc.docstatus===1) {
if (cint(frappe.defaults.get_default("auto_accounting_for_stock"))) { this.show_stock_ledger();
this.show_general_ledger(); if (cint(frappe.defaults.get_default("auto_accounting_for_stock"))) {
this.show_general_ledger();
}
} }
if(!this.frm.doc.is_return) {
if(!this.frm.doc.is_return && this.frm.doc.status!="Closed") {
if(this.frm.doc.docstatus==0) { if(this.frm.doc.docstatus==0) {
cur_frm.add_custom_button(__('From Purchase Order'), cur_frm.add_custom_button(__('From Purchase Order'),
function() { function() {
@ -43,7 +46,7 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
get_query_filters: { get_query_filters: {
supplier: cur_frm.doc.supplier || undefined, supplier: cur_frm.doc.supplier || undefined,
docstatus: 1, docstatus: 1,
status: ["!=", "Stopped"], status: ["not in", ["Stopped", "Closed"]],
per_received: ["<", 99.99], per_received: ["<", 99.99],
company: cur_frm.doc.company company: cur_frm.doc.company
} }
@ -51,14 +54,20 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
}); });
} }
if(this.frm.doc.docstatus == 1) { if(this.frm.doc.docstatus == 1 && this.frm.doc.status!="Closed") {
cur_frm.add_custom_button(__('Return'), this.make_purchase_return); cur_frm.add_custom_button(__('Return'), this.make_purchase_return);
if(this.frm.doc.__onload && !this.frm.doc.__onload.billing_complete) { if(this.frm.doc.__onload && !this.frm.doc.__onload.billing_complete) {
cur_frm.add_custom_button(__('Invoice'), this.make_purchase_invoice).addClass("btn-primary"); cur_frm.add_custom_button(__('Invoice'), this.make_purchase_invoice).addClass("btn-primary");
} }
cur_frm.add_custom_button(__("Close"), this.close_purchase_receipt)
} }
} }
if(this.frm.doc.docstatus==1 && this.frm.doc.status === "Closed") {
cur_frm.add_custom_button(__('Re-open'), this.reopen_purchase_receipt)
}
this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted==="Yes"); this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted==="Yes");
}, },
@ -122,12 +131,34 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
this.get_terms(); this.get_terms();
}, },
}); close_purchase_receipt: function() {
cur_frm.cscript.update_status("Closed");
},
reopen_purchase_receipt: function() {
cur_frm.cscript.update_status("Submitted");
}
});
// for backward compatibility: combine new and previous states // for backward compatibility: combine new and previous states
$.extend(cur_frm.cscript, new erpnext.stock.PurchaseReceiptController({frm: cur_frm})); $.extend(cur_frm.cscript, new erpnext.stock.PurchaseReceiptController({frm: cur_frm}));
cur_frm.cscript.update_status = function(status) {
frappe.ui.form.is_saving = true;
frappe.call({
method:"erpnext.stock.doctype.purchase_receipt.purchase_receipt.update_purchase_order_status",
args: {docname: cur_frm.doc.name, status: status},
callback: function(r){
if(!r.exc)
cur_frm.reload_doc();
},
always: function(){
frappe.ui.form.is_saving = false;
}
})
}
cur_frm.fields_dict['supplier_address'].get_query = function(doc, cdt, cdn) { cur_frm.fields_dict['supplier_address'].get_query = function(doc, cdt, cdn) {
return { return {
filters: { 'supplier': doc.supplier} filters: { 'supplier': doc.supplier}

View File

@ -88,7 +88,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Series", "label": "Series",
"no_copy": 1, "no_copy": 1,
"oldfieldname": "naming_series", "oldfieldname": "naming_series",
"oldfieldtype": "Select", "oldfieldtype": "Select",
@ -1062,7 +1062,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Apply Additional Discount On", "label": "Apply Additional Discount On",
"no_copy": 0, "no_copy": 0,
"options": "\nGrand Total\nNet Total", "options": "\nGrand Total\nNet Total",
"permlevel": 0, "permlevel": 0,
@ -1498,7 +1498,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Raw Materials Supplied", "label": "Raw Materials Supplied",
"no_copy": 0, "no_copy": 0,
"oldfieldname": "is_subcontracted", "oldfieldname": "is_subcontracted",
"oldfieldtype": "Select", "oldfieldtype": "Select",
@ -1643,11 +1643,11 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 1, "in_filter": 1,
"in_list_view": 0, "in_list_view": 0,
"label": "Status", "label": "Status",
"no_copy": 1, "no_copy": 1,
"oldfieldname": "status", "oldfieldname": "status",
"oldfieldtype": "Select", "oldfieldtype": "Select",
"options": "\nDraft\nSubmitted\nCancelled", "options": "\nDraft\nSubmitted\nCancelled\nClosed",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"print_width": "150px", "print_width": "150px",
@ -2076,7 +2076,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-10-02 07:39:05.186518", "modified": "2015-11-02 01:19:45.539793",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Purchase Receipt", "name": "Purchase Receipt",

View File

@ -11,6 +11,7 @@ import frappe.defaults
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
form_grid_templates = { form_grid_templates = {
"items": "templates/form_grid/item_grid.html" "items": "templates/form_grid/item_grid.html"
@ -67,7 +68,7 @@ class PurchaseReceipt(BuyingController):
pc_obj = frappe.get_doc('Purchase Common') pc_obj = frappe.get_doc('Purchase Common')
pc_obj.validate_for_items(self) pc_obj.validate_for_items(self)
self.check_for_stopped_status(pc_obj) self.check_for_stopped_or_closed_status(pc_obj)
# sub-contracting # sub-contracting
self.validate_for_subcontracting() self.validate_for_subcontracting()
@ -219,12 +220,12 @@ class PurchaseReceipt(BuyingController):
raise frappe.ValidationError raise frappe.ValidationError
# Check for Stopped status # Check for Stopped status
def check_for_stopped_status(self, pc_obj): def check_for_stopped_or_closed_status(self, pc_obj):
check_list =[] check_list =[]
for d in self.get('items'): for d in self.get('items'):
if d.meta.get_field('prevdoc_docname') and d.prevdoc_docname and d.prevdoc_docname not in check_list: if d.meta.get_field('prevdoc_docname') and d.prevdoc_docname and d.prevdoc_docname not in check_list:
check_list.append(d.prevdoc_docname) check_list.append(d.prevdoc_docname)
pc_obj.check_for_stopped_status(d.prevdoc_doctype, d.prevdoc_docname) pc_obj.check_for_stopped_or_closed_status(d.prevdoc_doctype, d.prevdoc_docname)
# on submit # on submit
def on_submit(self): def on_submit(self):
@ -260,7 +261,7 @@ class PurchaseReceipt(BuyingController):
def on_cancel(self): def on_cancel(self):
pc_obj = frappe.get_doc('Purchase Common') pc_obj = frappe.get_doc('Purchase Common')
self.check_for_stopped_status(pc_obj) self.check_for_stopped_or_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
@ -427,6 +428,10 @@ class PurchaseReceipt(BuyingController):
return process_gl_map(gl_entries) return process_gl_map(gl_entries)
def update_status(self, status):
self.set_status(update=True, status = status)
self.notify_update()
clear_doctype_notifications(self)
@frappe.whitelist() @frappe.whitelist()
def make_purchase_invoice(source_name, target_doc=None): def make_purchase_invoice(source_name, target_doc=None):
@ -487,3 +492,9 @@ def get_invoiced_qty_map(purchase_receipt):
def make_purchase_return(source_name, target_doc=None): def make_purchase_return(source_name, target_doc=None):
from erpnext.controllers.sales_and_purchase_return import make_return_doc from erpnext.controllers.sales_and_purchase_return import make_return_doc
return make_return_doc("Purchase Receipt", source_name, target_doc) return make_return_doc("Purchase Receipt", source_name, target_doc)
@frappe.whitelist()
def update_purchase_receipt_status(docname, status):
pr = frappe.get_doc("Purchase Receipt", docname)
pr.update_status(status)

View File

@ -1,9 +1,11 @@
frappe.listview_settings['Purchase Receipt'] = { frappe.listview_settings['Purchase Receipt'] = {
add_fields: ["supplier", "supplier_name", "base_grand_total", "is_subcontracted", add_fields: ["supplier", "supplier_name", "base_grand_total", "is_subcontracted",
"transporter_name", "is_return"], "transporter_name", "is_return", "status"],
get_indicator: function(doc) { get_indicator: function(doc) {
if(cint(doc.is_return)==1) { if(cint(doc.is_return)==1) {
return [__("Return"), "darkgrey", "is_return,=,Yes"]; return [__("Return"), "darkgrey", "is_return,=,Yes"];
} } else if(doc.status==="Closed") {
return [__("Closed"), "green", "status,=,Closed"];
}
} }
}; };

View File

@ -175,8 +175,16 @@ class TestPurchaseReceipt(unittest.TestCase):
"purchase_document_no": pr.name, "purchase_document_no": pr.name,
"delivery_document_no": return_pr.name "delivery_document_no": return_pr.name
}) })
def test_closed_purchase_receipt(self):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_purchase_receipt_status
pr = make_purchase_receipt(do_not_submit=True)
pr.submit()
update_purchase_receipt_status(pr.name, "Closed")
self.assertEquals(frappe.db.get_value("Purchase Receipt", pr.name, "status"), "Closed")
def get_gl_entries(voucher_type, voucher_no): def get_gl_entries(voucher_type, voucher_no):
return frappe.db.sql("""select account, debit, credit return frappe.db.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type=%s and voucher_no=%s from `tabGL Entry` where voucher_type=%s and voucher_no=%s

View File

@ -30,7 +30,7 @@ def get_item_details(args):
"is_subcontracted": "Yes" / "No", "is_subcontracted": "Yes" / "No",
"transaction_type": "selling", "transaction_type": "selling",
"ignore_pricing_rule": 0/1 "ignore_pricing_rule": 0/1
"project_name": "", "project_name": ""
} }
""" """
args = process_args(args) args = process_args(args)
@ -69,7 +69,7 @@ def get_item_details(args):
if args.get("is_subcontracted") == "Yes": if args.get("is_subcontracted") == "Yes":
out.bom = get_default_bom(args.item_code) out.bom = get_default_bom(args.item_code)
return out return out
def process_args(args): def process_args(args):
@ -172,7 +172,9 @@ def get_basic_details(args, item):
"base_amount": 0.0, "base_amount": 0.0,
"net_rate": 0.0, "net_rate": 0.0,
"net_amount": 0.0, "net_amount": 0.0,
"discount_percentage": 0.0 "discount_percentage": 0.0,
"supplier": item.default_supplier,
"delivered_by_supplier": item.delivered_by_supplier,
}) })
# if default specified in item is for another company, fetch from company # if default specified in item is for another company, fetch from company

View File

@ -7,12 +7,12 @@
"doctype": "Report", "doctype": "Report",
"idx": 1, "idx": 1,
"is_standard": "Yes", "is_standard": "Yes",
"modified": "2015-10-06 12:43:37.547654", "modified": "2015-11-04 12:00:40.085130",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Ordered Items To Be Delivered", "name": "Ordered Items To Be Delivered",
"owner": "Administrator", "owner": "Administrator",
"query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project_name` as \"Project\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.qty as \"Qty:Float:140\",\n `tabSales Order Item`.delivered_qty as \"Delivered Qty:Float:140\",\n (`tabSales Order Item`.qty - ifnull(`tabSales Order Item`.delivered_qty, 0)) as \"Qty to Deliver:Float:140\",\n `tabSales Order Item`.base_rate as \"Rate:Float:140\",\n `tabSales Order Item`.base_amount as \"Amount:Float:140\",\n ((`tabSales Order Item`.qty - ifnull(`tabSales Order Item`.delivered_qty, 0))*`tabSales Order Item`.base_rate) as \"Amount to Deliver:Float:140\",\n `tabBin`.actual_qty as \"Available Qty:Float:120\",\n `tabBin`.projected_qty as \"Projected Qty:Float:120\",\n `tabSales Order`.`delivery_date` as \"Expected Delivery Date:Date:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order Item`.item_group as \"Item Group:Link/Item Group:120\",\n `tabSales Order Item`.warehouse as \"Warehouse:Link/Warehouse:200\"\nfrom\n `tabSales Order` JOIN `tabSales Order Item` \n LEFT JOIN `tabBin` ON (`tabBin`.item_code = `tabSales Order Item`.item_code\n and `tabBin`.warehouse = `tabSales Order Item`.warehouse)\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status != \"Stopped\"\n and ifnull(`tabSales Order Item`.delivered_qty,0) < ifnull(`tabSales Order Item`.qty,0)\norder by `tabSales Order`.transaction_date asc", "query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project_name` as \"Project\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.qty as \"Qty:Float:140\",\n `tabSales Order Item`.delivered_qty as \"Delivered Qty:Float:140\",\n (`tabSales Order Item`.qty - ifnull(`tabSales Order Item`.delivered_qty, 0)) as \"Qty to Deliver:Float:140\",\n `tabSales Order Item`.base_rate as \"Rate:Float:140\",\n `tabSales Order Item`.base_amount as \"Amount:Float:140\",\n ((`tabSales Order Item`.qty - ifnull(`tabSales Order Item`.delivered_qty, 0))*`tabSales Order Item`.base_rate) as \"Amount to Deliver:Float:140\",\n `tabBin`.actual_qty as \"Available Qty:Float:120\",\n `tabBin`.projected_qty as \"Projected Qty:Float:120\",\n `tabSales Order`.`delivery_date` as \"Expected Delivery Date:Date:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order Item`.item_group as \"Item Group:Link/Item Group:120\",\n `tabSales Order Item`.warehouse as \"Warehouse:Link/Warehouse:200\"\nfrom\n `tabSales Order` JOIN `tabSales Order Item` \n LEFT JOIN `tabBin` ON (`tabBin`.item_code = `tabSales Order Item`.item_code\n and `tabBin`.warehouse = `tabSales Order Item`.warehouse)\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status not in (\"Stopped\", \"Closed\")\n and ifnull(`tabSales Order Item`.delivered_qty,0) < ifnull(`tabSales Order Item`.qty,0)\norder by `tabSales Order`.transaction_date asc",
"ref_doctype": "Delivery Note", "ref_doctype": "Delivery Note",
"report_name": "Ordered Items To Be Delivered", "report_name": "Ordered Items To Be Delivered",
"report_type": "Query Report" "report_type": "Query Report"

View File

@ -2,16 +2,17 @@
"add_total_row": 1, "add_total_row": 1,
"apply_user_permissions": 1, "apply_user_permissions": 1,
"creation": "2013-02-22 18:01:55", "creation": "2013-02-22 18:01:55",
"disabled": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Report", "doctype": "Report",
"idx": 1, "idx": 1,
"is_standard": "Yes", "is_standard": "Yes",
"modified": "2015-03-30 05:47:04.213199", "modified": "2015-11-04 12:01:22.108641",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Purchase Order Items To Be Received", "name": "Purchase Order Items To Be Received",
"owner": "Administrator", "owner": "Administrator",
"query": "select \n `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n\t`tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order Item`.`schedule_date` as \"Reqd by Date:Date:110\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order Item`.`project_name` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.qty as \"Qty:Float:100\",\n\t`tabPurchase Order Item`.received_qty as \"Received Qty:Float:100\", \n\t(`tabPurchase Order Item`.qty - ifnull(`tabPurchase Order Item`.received_qty, 0)) as \"Qty to Receive:Float:100\",\n `tabPurchase Order Item`.warehouse as \"Warehouse:Link/Warehouse:150\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\",\n `tabPurchase Order Item`.brand as \"Brand::100\",\n\t`tabPurchase Order`.`company` as \"Company:Link/Company:\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status != \"Stopped\"\n\tand ifnull(`tabPurchase Order Item`.received_qty, 0) < ifnull(`tabPurchase Order Item`.qty, 0)\norder by `tabPurchase Order`.transaction_date asc", "query": "select \n `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n\t`tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order Item`.`schedule_date` as \"Reqd by Date:Date:110\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order Item`.`project_name` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.qty as \"Qty:Float:100\",\n\t`tabPurchase Order Item`.received_qty as \"Received Qty:Float:100\", \n\t(`tabPurchase Order Item`.qty - ifnull(`tabPurchase Order Item`.received_qty, 0)) as \"Qty to Receive:Float:100\",\n `tabPurchase Order Item`.warehouse as \"Warehouse:Link/Warehouse:150\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\",\n `tabPurchase Order Item`.brand as \"Brand::100\",\n\t`tabPurchase Order`.`company` as \"Company:Link/Company:\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status not in (\"Stopped\", \"Closed\")\n\tand ifnull(`tabPurchase Order Item`.received_qty, 0) < ifnull(`tabPurchase Order Item`.qty, 0)\norder by `tabPurchase Order`.transaction_date asc",
"ref_doctype": "Purchase Receipt", "ref_doctype": "Purchase Receipt",
"report_name": "Purchase Order Items To Be Received", "report_name": "Purchase Order Items To Be Received",
"report_type": "Query Report" "report_type": "Query Report"

View File

@ -90,7 +90,7 @@ def get_reserved_qty(item_code, warehouse):
and parenttype="Sales Order" and parenttype="Sales Order"
and item_code != parent_item and item_code != parent_item
and exists (select * from `tabSales Order` so and exists (select * from `tabSales Order` so
where name = dnpi_in.parent and docstatus = 1 and status != 'Stopped') where name = dnpi_in.parent and docstatus = 1 and status not in ('Stopped','Closed'))
) dnpi) ) dnpi)
union union
(select qty as dnpi_qty, qty as so_item_qty, (select qty as dnpi_qty, qty as so_item_qty,
@ -99,7 +99,7 @@ def get_reserved_qty(item_code, warehouse):
where item_code = %s and warehouse = %s where item_code = %s and warehouse = %s
and exists(select * from `tabSales Order` so and exists(select * from `tabSales Order` so
where so.name = so_item.parent and so.docstatus = 1 where so.name = so_item.parent and so.docstatus = 1
and so.status != 'Stopped')) and so.status not in ('Stopped','Closed')))
) tab ) tab
where where
so_item_qty >= so_item_delivered_qty so_item_qty >= so_item_delivered_qty
@ -122,7 +122,7 @@ def get_ordered_qty(item_code, warehouse):
from `tabPurchase Order Item` po_item, `tabPurchase Order` po from `tabPurchase Order Item` po_item, `tabPurchase Order` po
where po_item.item_code=%s and po_item.warehouse=%s where po_item.item_code=%s and po_item.warehouse=%s
and po_item.qty > ifnull(po_item.received_qty, 0) and po_item.parent=po.name and po_item.qty > ifnull(po_item.received_qty, 0) and po_item.parent=po.name
and po.status!='Stopped' and po.docstatus=1""", (item_code, warehouse)) and po.status not in ('Stopped', 'Closed', 'Delivered') and po.docstatus=1""", (item_code, warehouse))
return flt(ordered_qty[0][0]) if ordered_qty else 0 return flt(ordered_qty[0][0]) if ordered_qty else 0

View File

@ -67,16 +67,17 @@ frappe.ready(function() {
$("[itemscope] .item-view-attribute .form-control").on("change", function() { $("[itemscope] .item-view-attribute .form-control").on("change", function() {
try { try {
var item_code = encodeURIComponent(get_item_code()); var item_code = encodeURIComponent(get_item_code());
} catch(e) { } catch(e) {
// unable to find variant // unable to find variant
// then chose the closest available one // then chose the closest available one
var attribute = $(this).attr("data-attribute"); var attribute = $(this).attr("data-attribute");
var attribute_value = $(this).val() var attribute_value = $(this).val()
var item_code = update_attribute_selectors(attribute, attribute_value); var item_code = find_closest_match(attribute, attribute_value);
if (!item_code) { if (!item_code) {
msgprint(__("Please select some other value for {0}", [attribute])) msgprint(__("Cannot find a matching Item. Please select some other value for {0}.", [attribute]))
throw e; throw e;
} }
} }
@ -99,9 +100,16 @@ var toggle_update_cart = function(qty) {
function get_item_code() { function get_item_code() {
if(window.variant_info) { if(window.variant_info) {
var attributes = get_selected_attributes(); var attributes = get_selected_attributes();
var no_of_attributes = Object.keys(attributes).length;
for(var i in variant_info) { for(var i in variant_info) {
var variant = variant_info[i]; var variant = variant_info[i];
if (variant.attributes.length < no_of_attributes) {
// the case when variant has less attributes than template
continue;
}
var match = true; var match = true;
for(var j in variant.attributes) { for(var j in variant.attributes) {
if(attributes[variant.attributes[j].attribute] if(attributes[variant.attributes[j].attribute]
@ -120,13 +128,15 @@ function get_item_code() {
} }
} }
function update_attribute_selectors(selected_attribute, selected_attribute_value) { function find_closest_match(selected_attribute, selected_attribute_value) {
// find the closest match keeping the selected attribute in focus and get the item code // find the closest match keeping the selected attribute in focus and get the item code
var attributes = get_selected_attributes(); var attributes = get_selected_attributes();
var previous_match_score = 0; var previous_match_score = 0;
var previous_no_of_attributes = 0;
var matched; var matched;
for(var i in variant_info) { for(var i in variant_info) {
var variant = variant_info[i]; var variant = variant_info[i];
var match_score = 0; var match_score = 0;
@ -142,9 +152,13 @@ function update_attribute_selectors(selected_attribute, selected_attribute_value
} }
} }
if (has_selected_attribute && (match_score > previous_match_score)) { if (has_selected_attribute
&& ((match_score > previous_match_score) || (match_score==previous_match_score && previous_no_of_attributes < variant.attributes.length))) {
previous_match_score = match_score; previous_match_score = match_score;
matched = variant; matched = variant;
previous_no_of_attributes = variant.attributes.length;
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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