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
__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",
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) {

View File

@ -8,7 +8,7 @@ from frappe import msgprint, _, scrub
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.utils import get_balance_on, get_account_currency
from erpnext.setup.utils import get_company_currency
from erpnext.accounts.party import get_party_account
class JournalEntry(AccountsController):
def __init__(self, arg1, arg2=None):
@ -27,6 +27,7 @@ class JournalEntry(AccountsController):
self.validate_cheque_info()
self.validate_entries_for_advance()
self.validate_multi_currency()
self.set_amounts_in_company_currency()
self.validate_debit_and_credit()
self.validate_against_jv()
self.validate_reference_doc()
@ -175,6 +176,9 @@ class JournalEntry(AccountsController):
against_voucher = frappe.db.get_value(d.reference_type, d.reference_name,
[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
if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
if (against_voucher[0] != d.party or against_voucher[1] != d.account):
@ -280,6 +284,7 @@ class JournalEntry(AccountsController):
self.set_exchange_rate()
def set_amounts_in_company_currency(self):
for d in self.get("accounts"):
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"))
@ -517,209 +522,116 @@ def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None):
}
@frappe.whitelist()
def get_payment_entry_from_sales_invoice(sales_invoice):
"""Returns new Journal Entry document as dict for given Sales Invoice"""
from erpnext.accounts.utils import get_balance_on
si = frappe.get_doc("Sales Invoice", sales_invoice)
def get_payment_entry_against_order(dt, dn):
ref_doc = frappe.get_doc(dt, dn)
# exchange rate
exchange_rate = get_exchange_rate(si.debit_to, si.party_account_currency, si.company,
si.doctype, si.name)
if flt(ref_doc.per_billed, 2) > 0:
frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
jv = get_payment_entry(si)
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
if dt == "Sales Order":
party_type = "Customer"
amount_field_party = "credit_in_account_currency"
amount_field_bank = "debit_in_account_currency"
else:
row2.debit_in_account_currency = si.outstanding_amount * exchange_rate
party_type = "Supplier"
amount_field_party = "debit_in_account_currency"
amount_field_bank = "credit_in_account_currency"
# set multi currency check
if row1.account_currency != si.company_currency or row2.account_currency != si.company_currency:
jv.multi_currency = 1
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 = get_party_account(party_type, ref_doc.get(party_type.lower()), ref_doc.company)
party_account_currency = get_account_currency(party_account)
exchange_rate = get_exchange_rate(party_account, party_account_currency, so.company)
if party_account_currency == so.company_currency:
amount = flt(so.base_grand_total) - flt(so.advance_paid)
if party_account_currency == ref_doc.company_currency:
amount = flt(ref_doc.base_grand_total) - flt(ref_doc.advance_paid)
else:
amount = flt(so.grand_total) - flt(so.advance_paid)
amount = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid)
# credit customer
row1 = jv.get("accounts")[0]
row1.account = party_account
row1.account_currency = party_account_currency
row1.party_type = "Customer"
row1.party = so.customer
row1.balance = get_balance_on(party_account)
row1.party_balance = get_balance_on(party=so.customer, party_type="Customer")
row1.credit_in_account_currency = amount
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()
return get_payment_entry(ref_doc, {
"party_type": party_type,
"party_account": party_account,
"party_account_currency": party_account_currency,
"amount_field_party": amount_field_party,
"amount_field_bank": amount_field_bank,
"amount": amount,
"remarks": 'Advance Payment received against {0} {1}'.format(dt, dn),
"is_advance": "Yes"
})
@frappe.whitelist()
def get_payment_entry_from_purchase_order(purchase_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
po = frappe.get_doc("Purchase Order", purchase_order)
if flt(po.per_billed, 2) != 0.0:
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)
def get_payment_entry_against_invoice(dt, dn):
ref_doc = frappe.get_doc(dt, dn)
if dt == "Sales Invoice":
party_type = "Customer"
party_account = ref_doc.debit_to
amount_field_party = "credit_in_account_currency"
amount_field_bank = "debit_in_account_currency"
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"
# credit customer
row1 = jv.get("accounts")[0]
row1.account = party_account
row1.party_type = "Supplier"
row1.party = po.supplier
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
row1.reference_type = po.doctype
row1.reference_name = po.name
row1.is_advance = "Yes"
row1.exchange_rate = exchange_rate
row1.account_type = "Payable"
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"
})
# debit bank
row2 = jv.get("accounts")[1]
if row2.account_currency == party_account_currency:
row2.credit_in_account_currency = amount
else:
row2.credit_in_account_currency = amount * exchange_rate
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)
# set multi currency check
if row1.account_currency != po.company_currency or row2.account_currency != po.company_currency:
jv.multi_currency = 1
jv = frappe.new_doc("Journal Entry")
jv.update({
"voucher_type": "Bank Entry",
"company": ref_doc.company,
"remark": args.get("remarks")
})
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")
party_row = jv.append("accounts", {
"account": args.get("party_account"),
"party_type": args.get("party_type"),
"party": ref_doc.get(args.get("party_type").lower()),
"cost_center": cost_center,
"account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"),
"account_currency": args.get("party_account_currency") or \
get_account_currency(args.get("party_account")),
"account_balance": get_balance_on(args.get("party_account")),
"party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")),
"exchange_rate": exchange_rate,
args.get("amount_field_party"): args.get("amount"),
"is_advance": args.get("is_advance"),
"reference_type": ref_doc.doctype,
"reference_name": ref_doc.name
})
bank_row = jv.append("accounts")
bank_account = get_default_bank_cash_account(ref_doc.company, "Bank Entry")
if bank_account:
d2.account = bank_account["account"]
d2.balance = bank_account["balance"]
d2.account_currency = bank_account["account_currency"]
d2.account_type = bank_account["account_type"]
d2.exchange_rate = get_exchange_rate(bank_account["account"],
bank_account["account_currency"], doc.company)
d2.cost_center = cost_center
bank_row.update(bank_account)
bank_row.exchange_rate = get_exchange_rate(bank_account["account"],
bank_account["account_currency"], ref_doc.company)
return jv
bank_row.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)
# 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()
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"):
frappe.msgprint(_("No Permission"), raise_exception=1)
from erpnext.accounts.party import get_party_account
account = get_party_account(party_type, party, company)
account_balance = get_balance_on(account=account)

View File

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

View File

@ -48,7 +48,7 @@ class PurchaseInvoice(BuyingController):
self.check_conversion_rate()
self.validate_credit_to_acc()
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_uom_is_integer("uom", "qty")
self.set_against_expense_account()
@ -103,14 +103,14 @@ class PurchaseInvoice(BuyingController):
self.party_account_currency = account.account_currency
def check_for_stopped_status(self):
def check_for_stopped_or_closed_status(self):
check_list = []
pc_obj = frappe.get_doc('Purchase Common')
for d in self.get('items'):
if d.purchase_order and not d.purchase_order in check_list and not d.purchase_receipt:
check_list.append(d.purchase_order)
stopped = frappe.db.sql("select name from `tabPurchase Order` where status = 'Stopped' and name = %s", d.purchase_order)
if stopped:
throw(_("Purchase Order {0} is 'Stopped'").format(d.purchase_order))
pc_obj.check_for_stopped_or_closed_status('Purchase Order', d.purchase_order)
def validate_with_previous_doc(self):
super(PurchaseInvoice, self).validate_with_previous_doc({
@ -394,6 +394,8 @@ class PurchaseInvoice(BuyingController):
make_gl_entries(gl_entries, cancel=(self.docstatus == 2))
def on_cancel(self):
self.check_for_stopped_or_closed_status()
if not self.is_return:
from erpnext.accounts.utils import remove_against_link_from_jv
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.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'),
this.make_sales_return);
@ -61,7 +68,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
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");
}
}
@ -104,7 +111,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
source_doctype: "Sales Order",
get_query_filters: {
docstatus: 1,
status: ["!=", "Stopped"],
status: ["not in", ["Stopped", "Closed"]],
per_billed: ["<", 99.99],
customer: cur_frm.doc.customer || undefined,
company: cur_frm.doc.company
@ -323,9 +330,10 @@ cur_frm.cscript['Make Delivery Note'] = function() {
cur_frm.cscript.make_bank_entry = function() {
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: {
"sales_invoice": cur_frm.doc.name
"dt": "Sales Invoice",
"dn": cur_frm.doc.name
},
callback: function(r) {
var doclist = frappe.model.sync(r.message);

View File

@ -2951,7 +2951,7 @@
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"modified": "2015-10-02 07:39:09.123982",
"modified": "2015-10-26 12:12:40.616546",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@ -52,7 +52,7 @@ class SalesInvoice(SellingController):
self.validate_proj_cust()
self.validate_with_previous_doc()
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_fixed_asset_account()
self.clear_unallocated_advances("Sales Invoice Advance", "advances")
@ -117,7 +117,7 @@ class SalesInvoice(SellingController):
if cint(self.update_stock) == 1:
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
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",
"so_detail": "so_detail"
},
"postprocess": update_item
"postprocess": update_item,
"condition": lambda doc: doc.delivered_by_supplier!=1
},
"Sales Taxes and Charges": {
"doctype": "Sales Taxes and Charges",

View File

@ -680,6 +680,51 @@
"set_only_once": 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,
"bold": 0,
@ -1282,7 +1327,7 @@
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"modified": "2015-10-19 03:04:52.093181",
"modified": "2015-11-02 15:14:02.306067",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",

View File

@ -1,16 +1,18 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2013-07-30 17:28:49",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 1,
"is_standard": "Yes",
"modified": "2015-03-30 05:33:45.353064",
"modified": "2015-11-02 12:32:02.048551",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Delivered Items To Be Billed",
"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",
"report_name": "Delivered Items To Be Billed",
"report_type": "Query Report"

View File

@ -1,16 +1,18 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2013-02-21 14:26:44",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 1,
"is_standard": "Yes",
"modified": "2015-03-30 05:33:29.382709",
"modified": "2015-11-04 11:56:32.699103",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Ordered Items To Be Billed",
"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",
"report_name": "Ordered Items To Be Billed",
"report_type": "Query Report"

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe import _
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):
if not filters: filters = {}
@ -13,24 +13,26 @@ def execute(filters=None):
columns = get_columns(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 = ""
data = []
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":
payment_amount = flt(d.debit) or -1 * flt(d.credit)
else:
payment_amount = flt(d.credit) or -1 * flt(d.debit)
row = [d.name, d.party_type, d.party, d.posting_date, d.reference_name,
against_date, d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark]
row = [d.name, d.party_type, d.party, d.posting_date, d.reference_name, invoice.posting_date,
invoice.due_date, d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark]
if d.reference_name:
row += get_ageing_data(30, 60, 90, d.posting_date, against_date, payment_amount)
else:
row += ["", "", "", "", ""]
if invoice.due_date:
row.append((getdate(d.posting_date) - getdate(invoice.due_date)).days or 0)
data.append(row)
@ -43,13 +45,25 @@ def validate_filters(filters):
.format(filters.payment_type, filters.party_type))
def get_columns(filters):
return [_("Journal Entry") + ":Link/Journal Entry:140",
_("Party Type") + "::100", _("Party") + ":Dynamic Link/Party Type:140",
return [
_("Journal Entry") + ":Link/Journal Entry:140",
_("Party Type") + "::100",
_("Party") + ":Dynamic Link/Party Type:140",
_("Posting Date") + ":Date:100",
_("Against 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",
_("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"
_("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == "Outgoing" else ":Link/Sales Invoice:130"),
_("Invoice Posting Date") + ":Date:130",
_("Payment Due Date") + ":Date:130",
_("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):
@ -67,6 +81,13 @@ def get_conditions(filters):
if filters.get("party"):
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"):
conditions.append("jv.company=%(company)s")
@ -89,12 +110,9 @@ def get_entries(filters):
return entries
def get_invoice_posting_date_map(filters):
invoice_posting_date_map = {}
if filters.get("payment_type") == "Incoming":
for t in frappe.db.sql("""select name, posting_date from `tabSales Invoice`"""):
invoice_posting_date_map[t[0]] = t[1]
else:
for t in frappe.db.sql("""select name, posting_date from `tabPurchase Invoice`"""):
invoice_posting_date_map[t[0]] = t[1]
invoice_details = {}
dt = "Sales Invoice" if filters.get("payment_type") == "Incoming" else "Purchase Invoice"
for t in frappe.db.sql("select name, posting_date, due_date from `tab{0}`".format(dt), as_dict=1):
invoice_details[t.name] = t
return invoice_posting_date_map
return invoice_details

View File

@ -2,16 +2,17 @@
"add_total_row": 1,
"apply_user_permissions": 1,
"creation": "2013-05-28 15:54:16",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 1,
"is_standard": "Yes",
"modified": "2015-03-30 05:37:23.626083",
"modified": "2015-11-04 11:56:14.321664",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Order Items To Be Billed",
"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",
"report_name": "Purchase Order Items To Be Billed",
"report_type": "Query Report"

View File

@ -1,16 +1,18 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2013-07-30 18:35:10",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 1,
"is_standard": "Yes",
"modified": "2015-04-14 11:56:02.323769",
"modified": "2015-11-02 12:33:11.681513",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Received Items To Be Billed",
"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",
"report_name": "Received Items To Be Billed",
"report_type": "Query Report"

View File

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

View File

@ -19,10 +19,17 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
this._super();
// 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)
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Purchase Order']);
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100) {
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) {
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();
}
if(doc.docstatus == 1 && doc.status == 'Stopped')
cur_frm.add_custom_button(__('Unstop'), cur_frm.cscript['Unstop Purchase Order']);
if(doc.docstatus == 1 && in_list(["Stopped", "Closed", "Delivered"], doc.status)) {
cur_frm.add_custom_button(__('Re-open'), this.unstop_purchase_order);
}
},
make_stock_entry: function() {
@ -146,15 +154,28 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
make_bank_entry: function() {
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: {
"purchase_order": cur_frm.doc.name
"dt": "Purchase Order",
"dn": cur_frm.doc.name
},
callback: function(r) {
var doclist = frappe.model.sync(r.message);
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
$.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) {
return {
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){
//function to make row of table

View File

@ -151,6 +151,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"fieldname": "address_display",
"fieldtype": "Small Text",
"hidden": 1,
@ -326,6 +327,255 @@
"set_only_once": 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,
"bold": 0,
@ -1479,7 +1729,7 @@
"no_copy": 1,
"oldfieldname": "status",
"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,
"print_hide": 1,
"read_only": 1,
@ -2033,7 +2283,7 @@
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"modified": "2015-10-16 06:13:50.058318",
"modified": "2015-11-04 04:44:22.025827",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",

View File

@ -39,7 +39,7 @@ class PurchaseOrder(BuyingController):
self.set_status()
pc_obj = frappe.get_doc('Purchase Common')
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("stock_uom", ["qty", "required_qty"])
@ -108,12 +108,12 @@ class PurchaseOrder(BuyingController):
= d.rate = item_last_purchase_rate
# Check for Stopped status
def check_for_stopped_status(self, pc_obj):
def check_for_stopped_or_closed_status(self, pc_obj):
check_list =[]
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:
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):
material_request_map = {}
@ -154,14 +154,16 @@ class PurchaseOrder(BuyingController):
def update_status(self, status):
self.check_modified_date()
self.db_set('status', status)
self.set_status(update=True)
self.set_status(update=True, status=status)
self.update_requested_qty()
self.update_ordered_qty()
self.notify_update()
clear_doctype_notifications(self)
def on_submit(self):
if self.delivered_by_supplier == 1:
self.update_status_updater()
super(PurchaseOrder, self).on_submit()
purchase_controller = frappe.get_doc("Purchase Common")
@ -176,8 +178,11 @@ class PurchaseOrder(BuyingController):
purchase_controller.update_last_purchase_rate(self, is_submit = 1)
def on_cancel(self):
if self.delivered_by_supplier == 1:
self.update_status_updater()
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
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"):
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()
def stop_or_unstop_purchase_orders(names, status):
if not frappe.has_permission("Purchase Order", "write"):
@ -223,11 +250,11 @@ def stop_or_unstop_purchase_orders(names, status):
for name in names:
po = frappe.get_doc("Purchase Order", name)
if po.docstatus == 1:
if status=="Stopped":
if po.status not in ("Stopped", "Cancelled") and (po.per_received < 100 or po.per_billed < 100):
po.update_status("Stopped")
if status in ("Stopped", "Closed"):
if po.status not in ("Stopped", "Cancelled", "Closed") and (po.per_received < 100 or po.per_billed < 100):
po.update_status(status)
else:
if po.status == "Stopped":
if po.status in ("Stopped", "Closed"):
po.update_status("Draft")
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.get_items()
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) {
if(doc.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) {
return [__("To Receive and Bill"), "orange",
"per_received,<,100|per_billed,<,100|status,!=,Stopped"];
@ -21,13 +25,16 @@ frappe.listview_settings['Purchase Order'] = {
onload: function(listview) {
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.page.add_menu_item(__("Set as Unstopped"), function() {
listview.page.add_menu_item(__("Re-open"), function() {
listview.call_for_selected_items(method, {"status": "Submitted"});
});
}
};

View File

@ -71,6 +71,20 @@ class TestPurchaseOrder(unittest.TestCase):
po = create_purchase_order(qty=3.4, do_not_save=True)
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):
po = frappe.new_doc("Purchase Order")
args = frappe._dict(args)

View File

@ -848,15 +848,16 @@
"bold": 0,
"collapsible": 0,
"fieldname": "prevdoc_doctype",
"fieldtype": "Data",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Prevdoc DocType",
"label": "Reference Document Type",
"no_copy": 1,
"oldfieldname": "prevdoc_doctype",
"oldfieldtype": "Data",
"options": "DocType",
"permlevel": 0,
"print_hide": 1,
"read_only": 1,
@ -871,16 +872,16 @@
"bold": 0,
"collapsible": 0,
"fieldname": "prevdoc_docname",
"fieldtype": "Link",
"fieldtype": "Dynamic Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 1,
"in_list_view": 0,
"label": "Material Request No",
"label": "Reference Name",
"no_copy": 1,
"oldfieldname": "prevdoc_docname",
"oldfieldtype": "Link",
"options": "Material Request",
"options": "prevdoc_doctype",
"permlevel": 0,
"print_hide": 1,
"print_width": "120px",
@ -1200,7 +1201,7 @@
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"modified": "2015-10-19 03:04:51.773011",
"modified": "2015-10-19 03:04:51.773012",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",

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"
},
"Learn": {
"color": "#FCB868",
"color": "#FF888B",
"force_show": True,
"icon": "icon-facetime-video",
"icon": "octicon octicon-device-camera-video",
"type": "module",
"is_help": True
}

View File

@ -7,6 +7,11 @@ def get_data():
"label": _("Documents"),
"icon": "icon-star",
"items": [
{
"type": "doctype",
"name": "Item",
"description": _("All Products or Services."),
},
{
"type": "doctype",
"name": "Material Request",
@ -32,11 +37,6 @@ def get_data():
"name": "Installation Note",
"description": _("Installation record for a Serial No.")
},
{
"type": "doctype",
"name": "Item",
"description": _("All Products or Services."),
},
{
"type": "doctype",
"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
from `tabDelivery Note`
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
`tabDelivery Note Item`.parent=`tabDelivery Note`.name), 0) >
ifnull((select sum(qty) from `tabSales Invoice Item` where

View File

@ -163,7 +163,7 @@ def validate_recurring_document(doc):
raise_exception=1)
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):

View File

@ -219,12 +219,12 @@ class SellingController(StockController):
so_warehouse = so_item and so_item[0]["warehouse"] or ""
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"):
if d.get(ref_fieldname):
status = frappe.db.get_value("Sales Order", d.get(ref_fieldname), "status")
if status == "Stopped":
frappe.throw(_("Sales Order {0} is stopped").format(d.get(ref_fieldname)))
if status in ("Stopped", "Closed"):
frappe.throw(_("Sales Order {0} is {1}").format(d.get(ref_fieldname), status))
def check_active_sales_items(obj):
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"],
["Stopped", "eval:self.status=='Stopped'"],
["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed'"],
],
"Purchase Order": [
["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 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"],
["Delivered", "eval:self.status=='Delivered'"],
["Stopped", "eval:self.status=='Stopped'"],
["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed'"],
],
"Delivery Note": [
["Draft", None],
["Submitted", "eval:self.docstatus==1"],
["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed'"],
],
"Purchase Receipt": [
["Draft", None],
["Submitted", "eval:self.docstatus==1"],
["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed'"],
]
}
@ -71,12 +76,16 @@ class StatusUpdater(Document):
self.update_qty()
self.validate_qty()
def set_status(self, update=False):
def set_status(self, update=False, status=None):
if self.is_new():
return
if self.doctype in status_map:
_status = self.status
if status and update:
self.db_set("status", status)
sl = status_map[self.doctype][:]
sl.reverse()
for s in sl:
@ -168,7 +177,6 @@ class StatusUpdater(Document):
else:
args['cond'] = ' and parent!="%s"' % self.name.replace('"', '\"')
args['set_modified'] = ''
if change_modified:
args['set_modified'] = ', modified = now(), modified_by = "{0}"'\
.format(frappe.db.escape(frappe.session.user))
@ -252,9 +260,9 @@ class StatusUpdater(Document):
zero_amount_refdoc.append(item.get(ref_fieldname))
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:
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])

View File

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

View File

@ -78,25 +78,22 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "leave_type",
"fieldtype": "Link",
"fieldname": "column_break1",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 1,
"in_list_view": 1,
"label": "Leave Type",
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"oldfieldname": "leave_type",
"oldfieldtype": "Link",
"options": "Leave Type",
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
"unique": 0,
"width": "50%"
},
{
"allow_on_submit": 0,
@ -126,62 +123,38 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break1",
"fieldtype": "Column Break",
"fieldname": "section_break_6",
"fieldtype": "Section 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,
"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
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "fiscal_year",
"fieldname": "leave_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 1,
"in_list_view": 0,
"label": "Fiscal Year",
"in_list_view": 1,
"label": "Leave Type",
"no_copy": 0,
"oldfieldname": "fiscal_year",
"oldfieldtype": "Data",
"options": "Fiscal Year",
"oldfieldname": "leave_type",
"oldfieldtype": "Link",
"options": "Leave Type",
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
@ -195,19 +168,20 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "carry_forward",
"fieldtype": "Check",
"fieldname": "from_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Carry Forward",
"label": "From Date",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
@ -216,18 +190,39 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "carry_forward",
"fieldname": "carry_forwarded_leaves",
"fieldtype": "Float",
"fieldname": "to_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Carry Forwarded Leaves",
"label": "To Date",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"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,
"reqd": 0,
"search_index": 0,
@ -255,6 +250,50 @@
"set_only_once": 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,
"bold": 0,
@ -310,7 +349,7 @@
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"modified": "2015-10-02 07:38:55.314632",
"modified": "2015-11-04 03:13:11.121463",
"modified_by": "Administrator",
"module": "HR",
"name": "Leave Allocation",
@ -359,7 +398,7 @@
],
"read_only": 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_order": "DESC"
}

View File

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

@ -3,7 +3,8 @@
"docstatus": 1,
"doctype": "Leave Allocation",
"employee": "_T-Employee-0001",
"fiscal_year": "_Test Fiscal Year 2013",
"from_date": "2013-01-01",
"to_date": "2013-12-31",
"leave_type": "_Test Leave Type",
"new_leaves_allocated": 15
},
@ -11,7 +12,8 @@
"docstatus": 1,
"doctype": "Leave Allocation",
"employee": "_T-Employee-0002",
"fiscal_year": "_Test Fiscal Year 2013",
"from_date": "2013-01-01",
"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.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) {
@ -53,10 +38,6 @@ frappe.ui.form.on("Leave Application", {
frm.trigger("get_leave_balance");
},
fiscal_year: function(frm) {
frm.trigger("get_leave_balance");
},
leave_type: function(frm) {
frm.trigger("get_leave_balance");
},
@ -85,12 +66,13 @@ frappe.ui.form.on("Leave Application", {
},
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({
method: "get_leave_balance",
args: {
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
}
});
@ -109,6 +91,7 @@ frappe.ui.form.on("Leave Application", {
callback: function(response) {
if (response && response.message) {
frm.set_value('total_leave_days', response.message.total_leave_days);
frm.trigger("get_leave_balance");
}
}
});

View File

@ -559,7 +559,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 3,
"modified": "2015-10-02 07:38:55.471712",
"modified": "2015-10-28 16:14:25.640730",
"modified_by": "Administrator",
"module": "HR",
"name": "Leave Application",

View File

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

View File

@ -145,16 +145,38 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "fiscal_year",
"fieldtype": "Link",
"fieldname": "from_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 1,
"in_filter": 0,
"in_list_view": 0,
"label": "Fiscal Year",
"label": "From Date",
"no_copy": 0,
"options": "Fiscal Year",
"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,
"read_only": 0,
"report_hide": 0,
@ -260,7 +282,7 @@
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"modified": "2015-06-05 11:38:19.994852",
"modified": "2015-10-28 16:23:57.733900",
"modified_by": "Administrator",
"module": "HR",
"name": "Leave Control Panel",

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals
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.model.document import Document
@ -27,10 +27,14 @@ class LeaveControlPanel(Document):
return e
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):
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):
self.validate_values()
leave_allocated_for = []
@ -45,8 +49,8 @@ class LeaveControlPanel(Document):
la.employee = cstr(d[0])
la.employee_name = frappe.db.get_value('Employee',cstr(d[0]),'employee_name')
la.leave_type = self.leave_type
la.fiscal_year = self.fiscal_year
la.posting_date = nowdate()
la.from_date = self.from_date
la.to_date = self.to_date
la.carry_forward = cint(self.carry_forward)
la.new_leaves_allocated = flt(self.no_of_days)
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.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
if(locals["Salary Slip"]) {
$.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);
}
});
}
cur_frm.cscript.make_bank_entry = function(doc,cdt,cdn){

View File

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

View File

@ -24,50 +24,45 @@ def execute(filters=None):
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]
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`
where docstatus=1 and employee in (%s)""" %
','.join(['%s']*len(employee_names)), employee_names, as_dict=True)
where docstatus=1 and employee in (%s) and from_date >= '%s' and to_date <= '%s'""" %
(','.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
from `tabLeave Application`
where status="Approved" and docstatus = 1 and employee in (%s)
group by employee, fiscal_year, leave_type""" %
','.join(['%s']*len(employee_names)), employee_names, as_dict=True)
and from_date >= '%s' and to_date <= '%s'
group by employee, leave_type""" %
(','.join(['%s']*len(employee_names)), filters.get("from_date"),
filters.get("to_date")), employee_names, as_dict=True)
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:
columns.append(_(leave_type) + " " + _("Allocated") + ":Float")
columns.append(_(leave_type) + " " + _("Opening") + ":Float")
columns.append(_(leave_type) + " " + _("Taken") + ":Float")
columns.append(_(leave_type) + " " + _("Balance") + ":Float")
data = {}
for d in allocations:
data.setdefault((d.fiscal_year, d.employee,
d.leave_type), frappe._dict()).allocation = d.total_leaves_allocated
data.setdefault((d.employee,d.leave_type), frappe._dict()).allocation = d.leaves_allocated
for d in applications:
data.setdefault((d.fiscal_year, d.employee,
d.leave_type), frappe._dict()).leaves = d.leaves
data.setdefault((d.employee, d.leave_type), frappe._dict()).leaves = d.leaves
result = []
for fiscal_year in fiscal_years:
for employee in employees:
row = [fiscal_year, employee.name, employee.employee_name, employee.department]
row = [employee.name, employee.employee_name, employee.department]
result.append(row)
for leave_type in leave_types:
tmp = data.get((fiscal_year, employee.name, leave_type), frappe._dict())
tmp = data.get((employee.name, leave_type), frappe._dict())
row.append(tmp.allocation or 0)
row.append(tmp.leaves or 0)
row.append((tmp.allocation or 0) - (tmp.leaves or 0))

View File

@ -83,7 +83,7 @@ erpnext.production_order = {
frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Production Order'],
"icon-exclamation", "btn-default");
} 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");
}
@ -239,18 +239,11 @@ $.extend(cur_frm.cscript, {
});
cur_frm.cscript['Stop Production Order'] = function() {
var doc = cur_frm.doc;
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();});
}
$c_obj(cur_frm.doc, 'stop_unstop', 'Stopped', function(r, rt) {cur_frm.refresh();});
}
cur_frm.cscript['Unstop Production Order'] = function() {
var doc = cur_frm.doc;
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();});
$c_obj(cur_frm.doc, 'stop_unstop', 'Unstopped', function(r, rt) {cur_frm.refresh();});
}
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']:
account = frappe.new_doc("Email Account")
mapping = {
"email_id": "mail_login",
"login_id_is_different": 1,
"email_id": "auto_email_id",
"login_id": "mail_login",
"password": "mail_password",
"footer": "footer",
"smtp_server": "mail_server",

View File

@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import frappe
def execute():
@ -11,9 +13,12 @@ def execute():
for m in frappe.get_all("Project Milestone", "*"):
if (m.milestone and m.milestone_date
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({
"doctype": "Task",
"subject": m.milestone,
"subject": subject,
"description": description if description!=subject else None,
"expected_start_date": m.milestone_date,
"status": "Open" if m.status=="Pending" else "Closed",
"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.attached_to_doctype = "Item"
file_data.attached_to_name = item_code
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
if not frappe.db.get_value("Item", item_code, "image"):

View File

@ -30,3 +30,6 @@ def execute():
frappe.reload_doctype("Sales Invoice")
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({
"hours": tl.hours,
"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):

View File

@ -115,6 +115,27 @@
"search_index": 0,
"set_only_once": 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,

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;
$.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]) {
msgprint(__("Please specify") + ": " +
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";
}
if(!args) {
if(frm.doc.customer) {
if(frm.doctype != "Purchase Order" && frm.doc.customer) {
args = {
party: frm.doc.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(!address_field) {
if(frm.doc.customer) {
if(frm.doctype != "Purchase Order" && frm.doc.customer) {
address_field = "customer_address";
} else if(frm.doc.supplier) {
address_field = "supplier_address";

View File

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

View File

@ -15,26 +15,45 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
refresh: function(doc, dt, dn) {
this._super();
this.frm.dashboard.reset();
var is_delivered_by_supplier = false;
var is_delivery_note = false;
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"),
// doc.per_delivered);
// cur_frm.dashboard.add_progress(cint(doc.per_billed) + __("% Billed"),
// doc.per_billed);
$.each(cur_frm.doc.items, function(i, item){
if(item.delivered_by_supplier == 1 || item.supplier){
if(item.qty > flt(item.ordered_qty))
is_delivered_by_supplier = true;
}
else{
if(item.qty > flt(item.delivered_qty))
is_delivery_note = true;
}
})
// indent
if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
// material request
if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1
&& 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) {
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry);
}
// stop
if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100)
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Sales Order'])
if(flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed) < 100) {
cur_frm.add_custom_button(__('Stop'), this.stop_sales_order)
}
cur_frm.add_custom_button(__('Close'), this.close_sales_order)
// maintenance
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
@ -43,17 +62,19 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
}
// delivery note
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
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 {
// 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() {
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: {
"sales_order": cur_frm.doc.name
"dt": "Sales Order",
"dn": cur_frm.doc.name
},
callback: function(r) {
var doclist = frappe.model.sync(r.message);
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 check = confirm(__("Are you sure you want to STOP ") + doc.name);
if (check) {
return $c('runserverobj', {
'method':'stop_sales_order',
'docs': doc
}, function(r,rt) {
cur_frm.refresh();
});
frappe.ui.form.is_saving = true;
frappe.call({
method: "erpnext.selling.doctype.sales_order.sales_order.update_status",
args: {status: status, name: doc.name},
callback: function(r){
cur_frm.reload_doc();
},
always: function() {
frappe.ui.form.is_saving = false;
}
});
}
cur_frm.cscript['Unstop Sales Order'] = function() {
var doc = cur_frm.doc;
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.update_status('Re-open', 'Draft')
}
cur_frm.cscript.on_submit = function(doc, cdt, cdn) {

View File

@ -234,6 +234,7 @@
"bold": 0,
"collapsible": 0,
"default": "Sales",
"depends_on": "",
"fieldname": "order_type",
"fieldtype": "Select",
"hidden": 0,
@ -381,6 +382,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"description": "",
"fieldname": "po_no",
"fieldtype": "Data",
@ -1977,7 +1979,7 @@
"no_copy": 1,
"oldfieldname": "status",
"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,
"print_hide": 1,
"read_only": 1,
@ -2553,7 +2555,7 @@
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"modified": "2015-10-03 07:39:10.525609",
"modified": "2015-10-22 16:32:34.339835",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",

View File

@ -31,6 +31,7 @@ class SalesOrder(SellingController):
self.validate_uom_is_integer("stock_uom", "qty")
self.validate_for_items()
self.validate_warehouse()
self.validate_drop_ship()
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
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
(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),
WarehouseRequired)
@ -147,6 +148,11 @@ class SalesOrder(SellingController):
doc.set_status(update=True)
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):
super(SalesOrder, self).on_submit()
@ -214,20 +220,13 @@ class SalesOrder(SellingController):
if date_diff and date_diff[0][0]:
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.db_set('status', 'Stopped')
self.set_status(update=True, status=status)
self.update_reserved_qty()
self.notify_update()
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):
"""update requested qty (before ordered_qty is updated)"""
item_wh_list = []
@ -253,6 +252,46 @@ class SalesOrder(SellingController):
def on_update(self):
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):
from erpnext.controllers.website_list_for_contact import get_list_context
list_context = get_list_context(context)
@ -268,12 +307,12 @@ def stop_or_unstop_sales_orders(names, status):
for name in names:
so = frappe.get_doc("Sales Order", name)
if so.docstatus == 1:
if status=="Stop":
if so.status not in ("Stopped", "Cancelled") and (so.per_delivered < 100 or so.per_billed < 100):
so.stop_sales_order()
if status in ("Stopped", "Closed"):
if so.status not in ("Stopped", "Cancelled", "Closed") and (so.per_delivered < 100 or so.per_billed < 100):
so.update_status(status)
else:
if so.status == "Stopped":
so.unstop_sales_order()
if so.status in ("Stopped", "Closed"):
so.update_status('Draft')
frappe.local.message_log = []
@ -350,7 +389,7 @@ def make_delivery_note(source_name, target_doc=None):
"parent": "against_sales_order",
},
"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": {
"doctype": "Sales Taxes and Charges",
@ -488,3 +527,96 @@ def get_events(start, end, filters=None):
"end": end
}, as_dict=True, update={"allDay": 0})
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") {
return [__("Stopped"), "darkgrey", "status,=,Stopped"];
} else if(doc.status==="Closed"){
return [__("Closed"), "green", "status,=,Closed"];
} else if (doc.order_type !== "Maintenance"
&& flt(doc.per_delivered, 2) < 100 && frappe.datetime.get_diff(doc.delivery_date) < 0) {
// to bill & overdue
@ -41,11 +44,15 @@ frappe.listview_settings['Sales Order'] = {
onload: function(listview) {
var method = "erpnext.selling.doctype.sales_order.sales_order.stop_or_unstop_sales_orders";
listview.page.add_menu_item(__("Set as Stopped"), function() {
listview.call_for_selected_items(method, {"status": "Stop"});
listview.page.add_menu_item(__("Close"), function() {
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"});
});

View File

@ -7,8 +7,6 @@ import frappe.permissions
import unittest
from erpnext.selling.doctype.sales_order.sales_order \
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
@ -97,12 +95,12 @@ class TestSalesOrder(unittest.TestCase):
# stop so
so.load_from_db()
so.stop_sales_order()
so.update_status("Stopped")
self.assertEqual(get_reserved_qty(), existing_reserved_qty)
# unstop so
so.load_from_db()
so.unstop_sales_order()
so.update_status('Draft')
self.assertEqual(get_reserved_qty(), existing_reserved_qty + 5)
dn.cancel()
@ -147,14 +145,14 @@ class TestSalesOrder(unittest.TestCase):
# stop so
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 Home Desktop 100"), existing_reserved_qty_item2)
# unstop so
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 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)
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):
so = frappe.new_doc("Sales Order")
args = frappe._dict(args)
@ -312,6 +426,11 @@ def make_sales_order(**args):
if "warehouse" not in args:
args.warehouse = "_Test Warehouse - _TC"
if args.item_list:
for item in args.item_list:
so.append("items", item)
else:
so.append("items", {
"item_code": args.item or args.item_code or "_Test Item",
"warehouse": args.warehouse,

View File

@ -683,6 +683,74 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "eval:doc.delivered_by_supplier==1||doc.supplier",
"fieldname": "drop_ship_section",
"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": 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": "delivered_by_supplier",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Supplier delivers to Customer",
"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": 1,
"bold": 0,
"collapsible": 0,
"fieldname": "supplier",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Supplier",
"no_copy": 0,
"options": "Supplier",
"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,
@ -708,6 +776,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "eval:doc.delivered_by_supplier!=1",
"fieldname": "warehouse",
"fieldtype": "Link",
"hidden": 0,
@ -734,6 +803,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "eval:doc.delivered_by_supplier!=1",
"fieldname": "target_warehouse",
"fieldtype": "Link",
"hidden": 0,
@ -917,6 +987,28 @@
"unique": 0,
"width": "70px"
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "ordered_qty",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Ordered Qty",
"no_copy": 1,
"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,
@ -1095,7 +1187,7 @@
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"modified": "2015-10-19 03:04:51.257808",
"modified": "2015-11-04 11:29:57.645383",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order Item",

View File

@ -111,7 +111,8 @@ class EmailDigest(Document):
"""Set standard digest style"""
context.text_muted = '#8D99A6'
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};
padding: 3px 7px; margin-right: 7px;'''.format(text_muted = context.text_muted)
context.section_head = 'margin-top: 60px; font-size: 16px;'
@ -152,7 +153,7 @@ class EmailDigest(Document):
todo_list = frappe.db.sql("""select *
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)
for t in todo_list:
@ -289,6 +290,7 @@ class EmailDigest(Document):
elif self.frequency == "Weekly":
# from date is the previous week's monday
from_date = today - timedelta(days=today.weekday(), weeks=1)
# to date is sunday i.e. the previous day
to_date = from_date + timedelta(days=6)
else:
@ -300,32 +302,18 @@ class EmailDigest(Document):
return from_date, to_date
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
if self.frequency == "Daily":
# from date, to_date is today
self.future_from_date = self.future_to_date = today
self.past_from_date = self.past_to_date = today - relativedelta(days = 1)
self.past_from_date = self.past_to_date = self.future_from_date - relativedelta(days = 1)
elif self.frequency == "Weekly":
# from date is the current week's monday
self.future_from_date = today - relativedelta(days=today.weekday())
# 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)
self.past_from_date = self.future_from_date - relativedelta(weeks=1)
self.past_to_date = self.future_from_date - relativedelta(days=1)
else:
# from date is the 1st day of the current month
self.future_from_date = today - relativedelta(days=today.day-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)
self.past_from_date = self.future_from_date - relativedelta(months=1)
self.past_to_date = self.future_from_date - relativedelta(days=1)
def get_next_sending(self):
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">
<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;">
<p>{% if frequency == "Daily" %}
{{ 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.find(".video-placeholder").on("click", function() {
window.erpnext_welcome_video_started = true;
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) {
this._super();
if (!doc.is_return) {
if (!doc.is_return && doc.status!="Closed") {
if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1)
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",
get_query_filters: {
docstatus: 1,
status: ["!=", "Stopped"],
status: ["not in", ["Stopped", "Closed"]],
per_delivered: ["<", 99.99],
project_name: cur_frm.doc.project_name || 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"))) {
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
var from_sales_invoice = false;
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");
}
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);
// 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) {
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 *****************
cur_frm.fields_dict['project_name'].get_query = function(doc, cdt, cdn) {
return {

View File

@ -2112,7 +2112,7 @@
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "\nDraft\nSubmitted\nCancelled",
"options": "\nDraft\nSubmitted\nCancelled\nClosed",
"permlevel": 0,
"print_hide": 1,
"print_width": "150px",
@ -2438,7 +2438,7 @@
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"modified": "2015-10-02 07:38:44.497411",
"modified": "2015-11-02 01:20:58.934509",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",

View File

@ -10,6 +10,8 @@ from frappe import msgprint, _
import frappe.defaults
from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.selling_controller import SellingController
from frappe.desk.notifications import clear_doctype_notifications
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@ -106,7 +108,7 @@ class DeliveryNote(SellingController):
self.set_status()
self.so_required()
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_warehouse()
self.validate_uom_is_integer("stock_uom", "qty")
@ -203,7 +205,7 @@ class DeliveryNote(SellingController):
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.update_prevdoc_status()
@ -268,6 +270,11 @@ class DeliveryNote(SellingController):
ps.cancel()
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):
from erpnext.controllers.website_list_for_contact import get_list_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):
from erpnext.controllers.sales_and_purchase_return import make_return_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'] = {
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) {
if(cint(doc.is_return)==1) {
return [__("Return"), "darkgrey", "is_return,=,Yes"];
} else if(doc.status==="Closed") {
return [__("Closed"), "green", "status,=,Closed"];
}
}
};

View File

@ -397,6 +397,15 @@ class TestDeliveryNote(unittest.TestCase):
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):
dn = frappe.new_doc("Delivery Note")
args = frappe._dict(args)

View File

@ -1188,6 +1188,28 @@
"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 (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,
"bold": 0,
@ -2135,7 +2157,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 1,
"modified": "2015-10-29 02:25:26.256373",
"modified": "2015-11-04 04:50:02.051468",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",

View File

@ -28,14 +28,6 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten
refresh: function(doc) {
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) {
cur_frm.add_custom_button(__("Get Items from BOM"),
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')
cur_frm.add_custom_button(__('Unstop Material Request'),
cur_frm.add_custom_button(__('Re-open'),
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() {
var doc = cur_frm.doc;
var check = confirm(__("Do you really want to STOP this Material Request?"));
if (check) {
return $c('runserverobj', args={'method':'update_status', 'arg': 'Stopped', 'docs': doc}, function(r,rt) {
$c('runserverobj', args={'method':'update_status', 'arg': 'Stopped', 'docs': doc}, function(r,rt) {
cur_frm.refresh();
});
}
};
cur_frm.cscript['Unstop Material Request'] = function(){
var doc = cur_frm.doc;
var check = confirm(__("Do you really want to UNSTOP this Material Request?"));
if (check) {
return $c('runserverobj', args={'method':'update_status', 'arg': 'Submitted','docs': doc}, function(r,rt) {
$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()
frappe.db.set(self, 'status', cstr(status))
self.update_requested_qty()
frappe.msgprint(_("Status updated to {0}").format(_(status)))
def on_cancel(self):
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')
self.update_requested_qty()

View File

@ -29,11 +29,14 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
refresh: function() {
this._super();
if(this.frm.doc.docstatus===1) {
this.show_stock_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) {
cur_frm.add_custom_button(__('From Purchase Order'),
function() {
@ -43,7 +46,7 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
get_query_filters: {
supplier: cur_frm.doc.supplier || undefined,
docstatus: 1,
status: ["!=", "Stopped"],
status: ["not in", ["Stopped", "Closed"]],
per_received: ["<", 99.99],
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);
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(__("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");
},
@ -122,12 +131,34 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
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
$.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) {
return {
filters: { 'supplier': doc.supplier}

View File

@ -1647,7 +1647,7 @@
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "\nDraft\nSubmitted\nCancelled",
"options": "\nDraft\nSubmitted\nCancelled\nClosed",
"permlevel": 0,
"print_hide": 1,
"print_width": "150px",
@ -2076,7 +2076,7 @@
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"modified": "2015-10-02 07:39:05.186518",
"modified": "2015-11-02 01:19:45.539793",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt",

View File

@ -11,6 +11,7 @@ import frappe.defaults
from erpnext.controllers.buying_controller import BuyingController
from erpnext.accounts.utils import get_account_currency
from frappe.desk.notifications import clear_doctype_notifications
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@ -67,7 +68,7 @@ class PurchaseReceipt(BuyingController):
pc_obj = frappe.get_doc('Purchase Common')
pc_obj.validate_for_items(self)
self.check_for_stopped_status(pc_obj)
self.check_for_stopped_or_closed_status(pc_obj)
# sub-contracting
self.validate_for_subcontracting()
@ -219,12 +220,12 @@ class PurchaseReceipt(BuyingController):
raise frappe.ValidationError
# Check for Stopped status
def check_for_stopped_status(self, pc_obj):
def check_for_stopped_or_closed_status(self, pc_obj):
check_list =[]
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:
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
def on_submit(self):
@ -260,7 +261,7 @@ class PurchaseReceipt(BuyingController):
def on_cancel(self):
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
submitted = frappe.db.sql("""select t1.name
from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2
@ -427,6 +428,10 @@ class PurchaseReceipt(BuyingController):
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()
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):
from erpnext.controllers.sales_and_purchase_return import make_return_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'] = {
add_fields: ["supplier", "supplier_name", "base_grand_total", "is_subcontracted",
"transporter_name", "is_return"],
"transporter_name", "is_return", "status"],
get_indicator: function(doc) {
if(cint(doc.is_return)==1) {
return [__("Return"), "darkgrey", "is_return,=,Yes"];
} else if(doc.status==="Closed") {
return [__("Closed"), "green", "status,=,Closed"];
}
}
};

View File

@ -176,6 +176,14 @@ class TestPurchaseReceipt(unittest.TestCase):
"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):
return frappe.db.sql("""select account, debit, credit

View File

@ -30,7 +30,7 @@ def get_item_details(args):
"is_subcontracted": "Yes" / "No",
"transaction_type": "selling",
"ignore_pricing_rule": 0/1
"project_name": "",
"project_name": ""
}
"""
args = process_args(args)
@ -172,7 +172,9 @@ def get_basic_details(args, item):
"base_amount": 0.0,
"net_rate": 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

View File

@ -7,12 +7,12 @@
"doctype": "Report",
"idx": 1,
"is_standard": "Yes",
"modified": "2015-10-06 12:43:37.547654",
"modified": "2015-11-04 12:00:40.085130",
"modified_by": "Administrator",
"module": "Stock",
"name": "Ordered Items To Be Delivered",
"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",
"report_name": "Ordered Items To Be Delivered",
"report_type": "Query Report"

View File

@ -2,16 +2,17 @@
"add_total_row": 1,
"apply_user_permissions": 1,
"creation": "2013-02-22 18:01:55",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 1,
"is_standard": "Yes",
"modified": "2015-03-30 05:47:04.213199",
"modified": "2015-11-04 12:01:22.108641",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Order Items To Be Received",
"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",
"report_name": "Purchase Order Items To Be Received",
"report_type": "Query Report"

View File

@ -90,7 +90,7 @@ def get_reserved_qty(item_code, warehouse):
and parenttype="Sales Order"
and item_code != parent_item
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)
union
(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
and exists(select * from `tabSales Order` so
where so.name = so_item.parent and so.docstatus = 1
and so.status != 'Stopped'))
and so.status not in ('Stopped','Closed')))
) tab
where
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
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.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

View File

@ -67,16 +67,17 @@ frappe.ready(function() {
$("[itemscope] .item-view-attribute .form-control").on("change", function() {
try {
var item_code = encodeURIComponent(get_item_code());
} catch(e) {
// unable to find variant
// then chose the closest available one
var attribute = $(this).attr("data-attribute");
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) {
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;
}
}
@ -99,9 +100,16 @@ var toggle_update_cart = function(qty) {
function get_item_code() {
if(window.variant_info) {
var attributes = get_selected_attributes();
var no_of_attributes = Object.keys(attributes).length;
for(var i in variant_info) {
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;
for(var j in variant.attributes) {
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
var attributes = get_selected_attributes();
var previous_match_score = 0;
var previous_no_of_attributes = 0;
var matched;
for(var i in variant_info) {
var variant = variant_info[i];
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;
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