Fixed Test Cases frappe/frappe#478
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 41be553..04a9959 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -6,14 +6,12 @@
import frappe.defaults
from frappe.utils import add_days, cint, cstr, date_diff, flt, getdate, nowdate, \
- get_first_day, get_last_day
-
-from frappe.utils import comma_and
+ get_first_day, get_last_day, comma_and
from frappe.model.naming import make_autoname
-
from frappe import _, msgprint
from erpnext.accounts.party import get_party_account, get_due_date
+from erpnext.controllers.stock_controller import update_gl_entries_after
month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12}
@@ -68,24 +66,24 @@
self.validate_c_form()
self.validate_time_logs_are_submitted()
self.validate_recurring_invoice()
- self.validate_multiple_billing("Delivery Note", "dn_detail", "amount",
+ self.validate_multiple_billing("Delivery Note", "dn_detail", "amount",
"delivery_note_details")
def on_submit(self):
- if cint(self.update_stock) == 1:
+ if cint(self.update_stock) == 1:
self.update_stock_ledger()
else:
# Check for Approving Authority
if not self.recurring_id:
- frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
+ frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
self.company, self.grand_total, self)
-
+
self.check_prev_docstatus()
-
+
self.update_status_updater_args()
self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
-
+
# this sequence because outstanding may get -ve
self.make_gl_entries()
self.check_credit_limit(self.debit_to)
@@ -103,18 +101,18 @@
def on_cancel(self):
if cint(self.update_stock) == 1:
self.update_stock_ledger()
-
+
self.check_stop_sales_order("sales_order")
-
+
from erpnext.accounts.utils import remove_against_link_from_jv
remove_against_link_from_jv(self.doctype, self.name, "against_invoice")
self.update_status_updater_args()
self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
-
+
self.make_cancel_gl_entries()
-
+
def update_status_updater_args(self):
if cint(self.update_stock):
self.status_updater.append({
@@ -133,31 +131,31 @@
'second_source_field': 'qty',
'second_join_field': 'prevdoc_detail_docname'
})
-
+
def on_update_after_submit(self):
self.validate_recurring_invoice()
self.convert_to_recurring()
-
+
def get_portal_page(self):
return "invoice" if self.docstatus==1 else None
-
+
def set_missing_values(self, for_validate=False):
self.set_pos_fields(for_validate)
-
+
if not self.debit_to:
self.debit_to = get_party_account(self.company, self.customer, "Customer")
if not self.due_date:
self.due_date = get_due_date(self.posting_date, self.customer, "Customer",
self.debit_to, self.company)
-
+
super(SalesInvoice, self).set_missing_values(for_validate)
-
+
def update_time_log_batch(self, sales_invoice):
for d in self.get(self.fname):
if d.time_log_batch:
tlb = frappe.get_doc("Time Log Batch", d.time_log_batch)
tlb.sales_invoice = sales_invoice
- tlb.update_after_submit()
+ tlb.save()
def validate_time_logs_are_submitted(self):
for d in self.get(self.fname):
@@ -171,10 +169,10 @@
"""Set retail related fields from pos settings"""
if cint(self.is_pos) != 1:
return
-
- from erpnext.stock.get_item_details import get_pos_settings_item_details, get_pos_settings
+
+ from erpnext.stock.get_item_details import get_pos_settings_item_details, get_pos_settings
pos = get_pos_settings(self.company)
-
+
if pos:
if not for_validate and not self.customer:
self.customer = pos.customer
@@ -184,31 +182,31 @@
'selling_price_list', 'company', 'select_print_heading', 'cash_bank_account'):
if (not for_validate) or (for_validate and not self.get(fieldname)):
self.set(fieldname, pos.get(fieldname))
-
+
if not for_validate:
self.update_stock = cint(pos.get("update_stock"))
# set pos values in items
for item in self.get("entries"):
if item.get('item_code'):
- for fname, val in get_pos_settings_item_details(pos,
+ for fname, val in get_pos_settings_item_details(pos,
frappe._dict(item.as_dict()), pos).items():
-
+
if (not for_validate) or (for_validate and not item.get(fname)):
item.set(fname, val)
- # fetch terms
+ # fetch terms
if self.tc_name and not self.terms:
self.terms = frappe.db.get_value("Terms and Conditions", self.tc_name, "terms")
-
+
# fetch charges
if self.taxes_and_charges and not len(self.get("other_charges")):
self.set_taxes("other_charges", "taxes_and_charges")
-
+
def get_advances(self):
- super(SalesInvoice, self).get_advances(self.debit_to,
+ super(SalesInvoice, self).get_advances(self.debit_to,
"Sales Invoice Advance", "advance_adjustment_details", "credit")
-
+
def get_company_abbr(self):
return frappe.db.sql("select abbr from tabCompany where name=%s", self.company)[0][0]
@@ -219,32 +217,32 @@
2. split into multiple rows if partially adjusted, assign against voucher
3. submit advance voucher
"""
-
+
lst = []
for d in self.get('advance_adjustment_details'):
if flt(d.allocated_amount) > 0:
args = {
- 'voucher_no' : d.journal_voucher,
- 'voucher_detail_no' : d.jv_detail_no,
- 'against_voucher_type' : 'Sales Invoice',
+ 'voucher_no' : d.journal_voucher,
+ 'voucher_detail_no' : d.jv_detail_no,
+ 'against_voucher_type' : 'Sales Invoice',
'against_voucher' : self.name,
- 'account' : self.debit_to,
- 'is_advance' : 'Yes',
- 'dr_or_cr' : 'credit',
+ 'account' : self.debit_to,
+ 'is_advance' : 'Yes',
+ 'dr_or_cr' : 'credit',
'unadjusted_amt' : flt(d.advance_amount),
'allocated_amt' : flt(d.allocated_amount)
}
lst.append(args)
-
+
if lst:
from erpnext.accounts.utils import reconcile_against_document
reconcile_against_document(lst)
-
+
def validate_customer_account(self):
"""Validates Debit To Account and Customer Matches"""
if self.customer and self.debit_to and not cint(self.is_pos):
acc_head = frappe.db.sql("select master_name from `tabAccount` where name = %s and docstatus != 2", self.debit_to)
-
+
if (acc_head and cstr(acc_head[0][0]) != cstr(self.customer)) or \
(not acc_head and (self.debit_to != cstr(self.customer) + " - " + self.get_company_abbr())):
msgprint("Debit To: %s do not match with Customer: %s for Company: %s.\n If both correctly entered, please select Master Type \
@@ -254,20 +252,20 @@
def validate_debit_acc(self):
if frappe.db.get_value("Account", self.debit_to, "report_type") != "Balance Sheet":
frappe.throw(_("Account must be a balance sheet account"))
-
+
def validate_fixed_asset_account(self):
"""Validate Fixed Asset and whether Income Account Entered Exists"""
for d in self.get('entries'):
- item = frappe.db.sql("""select name,is_asset_item,is_sales_item from `tabItem`
- where name = %s and (ifnull(end_of_life,'')='' or end_of_life = '0000-00-00'
+ item = frappe.db.sql("""select name,is_asset_item,is_sales_item from `tabItem`
+ where name = %s and (ifnull(end_of_life,'')='' or end_of_life = '0000-00-00'
or end_of_life > now())""", d.item_code)
- acc = frappe.db.sql("""select account_type from `tabAccount`
+ acc = frappe.db.sql("""select account_type from `tabAccount`
where name = %s and docstatus != 2""", d.income_account)
if not acc:
msgprint("Account: "+d.income_account+" does not exist in the system", raise_exception=True)
elif item and item[0][1] == 'Yes' and not acc[0][0] == 'Fixed Asset':
- msgprint("Please select income head with account type 'Fixed Asset' as Item %s is an asset item" % d.item_code, raise_exception=True)
-
+ msgprint("Please select income head with account type 'Fixed Asset' as Item %s is an asset item" % d.item_code, raise_exception=True)
+
def validate_with_previous_doc(self):
super(SalesInvoice, self).validate_with_previous_doc(self.tname, {
"Sales Order": {
@@ -281,7 +279,7 @@
["currency", "="]],
},
})
-
+
if cint(frappe.defaults.get_global_default('maintain_same_sales_rate')):
super(SalesInvoice, self).validate_with_previous_doc(self.tname, {
"Sales Order Item": {
@@ -296,7 +294,7 @@
"is_child_table": True
}
})
-
+
def set_aging_date(self):
if self.is_opening != 'Yes':
@@ -304,7 +302,7 @@
elif not self.aging_date:
msgprint("Aging Date is mandatory for opening entry")
raise Exception
-
+
def set_against_income_account(self):
"""Set against account for debit to account"""
@@ -333,8 +331,8 @@
def validate_proj_cust(self):
"""check for does customer belong to same project as entered.."""
if self.project_name and self.customer:
- res = frappe.db.sql("""select name from `tabProject`
- where name = %s and (customer = %s or
+ res = frappe.db.sql("""select name from `tabProject`
+ where name = %s and (customer = %s or
ifnull(customer,'')='')""", (self.project_name, self.customer))
if not res:
msgprint("Customer - %s does not belong to project - %s. \n\nIf you want to use project for multiple customers then please make customer details blank in that project."%(self.customer,self.project_name))
@@ -355,7 +353,7 @@
if not d.item_code:
msgprint("Please enter Item Code at line no : %s to update stock or remove check from Update Stock in Basic Info Tab." % (d.idx),
raise_exception=True)
-
+
def validate_delivery_note(self):
for d in self.get("entries"):
if d.delivery_note:
@@ -374,7 +372,7 @@
and parent = %s""", (self.amended_from, self.c_form_no))
frappe.db.set(self, 'c_form_no', '')
-
+
def update_current_stock(self):
for d in self.get('entries'):
if d.item_code and d.warehouse:
@@ -385,15 +383,15 @@
bin = frappe.db.sql("select actual_qty, projected_qty from `tabBin` where item_code = %s and warehouse = %s", (d.item_code, d.warehouse), as_dict = 1)
d.actual_qty = bin and flt(bin[0]['actual_qty']) or 0
d.projected_qty = bin and flt(bin[0]['projected_qty']) or 0
-
-
+
+
def get_warehouse(self):
- w = frappe.db.sql("""select warehouse from `tabPOS Setting`
- where ifnull(user,'') = %s and company = %s""",
+ w = frappe.db.sql("""select warehouse from `tabPOS Setting`
+ where ifnull(user,'') = %s and company = %s""",
(frappe.session['user'], self.company))
w = w and w[0][0] or ''
if not w:
- ps = frappe.db.sql("""select name, warehouse from `tabPOS Setting`
+ ps = frappe.db.sql("""select name, warehouse from `tabPOS Setting`
where ifnull(user,'') = '' and company = %s""", self.company)
if not ps:
msgprint("To make POS entry, please create POS Setting from Accounts --> POS Setting page and refresh the system.", raise_exception=True)
@@ -417,11 +415,11 @@
make_packing_list(self, 'entries')
else:
self.set('packing_details', [])
-
+
if cint(self.is_pos) == 1:
if flt(self.paid_amount) == 0:
- if self.cash_bank_account:
- frappe.db.set(self, 'paid_amount',
+ if self.cash_bank_account:
+ frappe.db.set(self, 'paid_amount',
(flt(self.grand_total) - flt(self.write_off_amount)))
else:
# show message that the amount is not paid
@@ -429,18 +427,18 @@
frappe.msgprint("Note: Payment Entry will not be created since 'Cash/Bank Account' was not specified.")
else:
frappe.db.set(self,'paid_amount',0)
-
+
def check_prev_docstatus(self):
for d in self.get('entries'):
if d.sales_order:
- submitted = frappe.db.sql("""select name from `tabSales Order`
+ submitted = frappe.db.sql("""select name from `tabSales Order`
where docstatus = 1 and name = %s""", d.sales_order)
if not submitted:
msgprint("Sales Order : "+ cstr(d.sales_order) +" is not submitted")
raise Exception , "Validation Error."
if d.delivery_note:
- submitted = frappe.db.sql("""select name from `tabDelivery Note`
+ submitted = frappe.db.sql("""select name from `tabDelivery Note`
where docstatus = 1 and name = %s""", d.delivery_note)
if not submitted:
msgprint("Delivery Note : "+ cstr(d.delivery_note) +" is not submitted")
@@ -455,45 +453,44 @@
"actual_qty": -1*flt(d.qty),
"stock_uom": frappe.db.get_value("Item", d.item_code, "stock_uom")
}))
-
+
self.make_sl_entries(sl_entries)
-
+
def make_gl_entries(self, repost_future_gle=True):
gl_entries = self.get_gl_entries()
-
+
if gl_entries:
from erpnext.accounts.general_ledger import make_gl_entries
-
+
update_outstanding = cint(self.is_pos) and self.write_off_account \
and 'No' or 'Yes'
- make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
+ make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
update_outstanding=update_outstanding, merge_entries=False)
if repost_future_gle and cint(self.update_stock) \
and cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
items, warehouse_account = self.get_items_and_warehouse_accounts()
- from controllers.stock_controller import update_gl_entries_after
- update_gl_entries_after(self.posting_date, self.posting_time,
+ update_gl_entries_after(self.posting_date, self.posting_time,
warehouse_account, items)
-
+
def get_gl_entries(self, warehouse_account=None):
from erpnext.accounts.general_ledger import merge_similar_entries
-
+
gl_entries = []
-
+
self.make_customer_gl_entry(gl_entries)
-
+
self.make_tax_gl_entries(gl_entries)
-
+
self.make_item_gl_entries(gl_entries)
-
+
# merge gl entries before adding pos entries
gl_entries = merge_similar_entries(gl_entries)
-
+
self.make_pos_gl_entries(gl_entries)
-
+
return gl_entries
-
+
def make_customer_gl_entry(self, gl_entries):
if self.grand_total:
gl_entries.append(
@@ -506,7 +503,7 @@
"against_voucher_type": self.doctype,
})
)
-
+
def make_tax_gl_entries(self, gl_entries):
for tax in self.get("other_charges"):
if flt(tax.tax_amount_after_discount_amount):
@@ -519,9 +516,9 @@
"cost_center": tax.cost_center
})
)
-
- def make_item_gl_entries(self, gl_entries):
- # income account gl entries
+
+ def make_item_gl_entries(self, gl_entries):
+ # income account gl entries
for item in self.get("entries"):
if flt(item.base_amount):
gl_entries.append(
@@ -533,12 +530,12 @@
"cost_center": item.cost_center
})
)
-
+
# expense account gl entries
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) \
and cint(self.update_stock):
gl_entries += super(SalesInvoice, self).get_gl_entries()
-
+
def make_pos_gl_entries(self, gl_entries):
if cint(self.is_pos) and self.cash_bank_account and self.paid_amount:
# POS, make payment entries
@@ -581,81 +578,81 @@
"cost_center": self.write_off_cost_center
})
)
-
+
def update_c_form(self):
"""Update amended id in C-form"""
if self.c_form_no and self.amended_from:
frappe.db.sql("""update `tabC-Form Invoice Detail` set invoice_no = %s,
invoice_date = %s, territory = %s, net_total = %s,
- grand_total = %s where invoice_no = %s and parent = %s""",
+ grand_total = %s where invoice_no = %s and parent = %s""",
(self.name, self.amended_from, self.c_form_no))
-
+
def validate_recurring_invoice(self):
if self.convert_into_recurring_invoice:
self.validate_notification_email_id()
-
+
if not self.recurring_type:
msgprint(_("Please select: ") + self.meta.get_label("recurring_type"),
raise_exception=1)
-
+
elif not (self.invoice_period_from_date and \
self.invoice_period_to_date):
msgprint(comma_and([self.meta.get_label("invoice_period_from_date"),
self.meta.get_label("invoice_period_to_date")])
+ _(": Mandatory for a Recurring Invoice."),
raise_exception=True)
-
+
def convert_to_recurring(self):
if self.convert_into_recurring_invoice:
if not self.recurring_id:
frappe.db.set(self, "recurring_id",
make_autoname("RECINV/.#####"))
-
+
self.set_next_date()
elif self.recurring_id:
frappe.db.sql("""update `tabSales Invoice`
set convert_into_recurring_invoice = 0
where recurring_id = %s""", (self.recurring_id,))
-
+
def validate_notification_email_id(self):
if self.notification_email_address:
email_list = filter(None, [cstr(email).strip() for email in
self.notification_email_address.replace("\n", "").split(",")])
-
+
from frappe.utils import validate_email_add
for email in email_list:
if not validate_email_add(email):
msgprint(self.meta.get_label("notification_email_address") \
+ " - " + _("Invalid Email Address") + ": \"%s\"" % email,
raise_exception=1)
-
+
else:
msgprint("Notification Email Addresses not specified for recurring invoice",
raise_exception=1)
-
+
def set_next_date(self):
""" Set next date on which auto invoice will be created"""
if not self.repeat_on_day_of_month:
- msgprint("""Please enter 'Repeat on Day of Month' field value.
- The day of the month on which auto invoice
+ msgprint("""Please enter 'Repeat on Day of Month' field value.
+ The day of the month on which auto invoice
will be generated e.g. 05, 28 etc.""", raise_exception=1)
-
+
next_date = get_next_date(self.posting_date,
month_map[self.recurring_type], cint(self.repeat_on_day_of_month))
-
+
frappe.db.set(self, 'next_date', next_date)
-
+
def get_next_date(dt, mcount, day=None):
dt = getdate(dt)
-
+
from dateutil.relativedelta import relativedelta
dt += relativedelta(months=mcount, day=day)
-
+
return dt
-
+
def manage_recurring_invoices(next_date=None, commit=True):
- """
+ """
Create recurring invoices on specific date by copying the original one
and notify the concerned people
"""
@@ -664,7 +661,7 @@
from `tabSales Invoice` where ifnull(convert_into_recurring_invoice, 0)=1
and docstatus=1 and next_date=%s
and next_date <= ifnull(end_date, '2199-12-31')""", next_date)
-
+
exception_list = []
for ref_invoice, recurring_id in recurring_invoices:
if not frappe.db.sql("""select name from `tabSales Invoice`
@@ -690,21 +687,20 @@
finally:
if commit:
frappe.db.begin()
-
+
if exception_list:
exception_message = "\n\n".join([cstr(d) for d in exception_list])
raise Exception, exception_message
def make_new_invoice(ref_wrapper, posting_date):
- from frappe.model.doc import clone
from erpnext.accounts.utils import get_fiscal_year
- new_invoice = clone(ref_wrapper)
-
+ new_invoice = frappe.copy_doc(ref_wrapper)
+
mcount = month_map[ref_wrapper.recurring_type]
-
+
invoice_period_from_date = get_next_date(ref_wrapper.invoice_period_from_date, mcount)
-
- # get last day of the month to maintain period if the from date is first day of its own month
+
+ # get last day of the month to maintain period if the from date is first day of its own month
# and to date is the last day of its own month
if (cstr(get_first_day(ref_wrapper.invoice_period_from_date)) == \
cstr(ref_wrapper.invoice_period_from_date)) and \
@@ -714,7 +710,7 @@
mcount))
else:
invoice_period_to_date = get_next_date(ref_wrapper.invoice_period_to_date, mcount)
-
+
new_invoice.update({
"posting_date": posting_date,
"aging_date": posting_date,
@@ -725,30 +721,30 @@
"fiscal_year": get_fiscal_year(posting_date)[0],
"owner": ref_wrapper.owner,
})
-
+
new_invoice.submit()
-
+
return new_invoice
-
+
def send_notification(new_rv):
"""Notify concerned persons about recurring invoice generation"""
-
+
from frappe.core.doctype.print_format.print_format import get_html
- frappe.sendmail(new_rv.notification_email_address,
- subject="New Invoice : " + new_rv.name,
+ frappe.sendmail(new_rv.notification_email_address,
+ subject="New Invoice : " + new_rv.name,
message = get_html(new_rv, new_rv, "SalesInvoice"))
-
+
def notify_errors(inv, customer, owner):
from frappe.utils.user import get_system_managers
recipients=get_system_managers()
-
+
frappe.sendmail(recipients + [frappe.db.get_value("User", owner, "email")],
subject="[Urgent] Error while creating recurring invoice for %s" % inv,
message = frappe.get_template("template/emails/recurring_invoice_failed.html").render({
"name": inv,
"customer": customer
}))
-
+
assign_task_to_owner(inv, "Recurring Invoice Failed", recipients)
def assign_task_to_owner(inv, msg, users):
@@ -776,64 +772,64 @@
def get_income_account(doctype, txt, searchfield, start, page_len, filters):
from erpnext.controllers.queries import get_match_cond
- # income account can be any Credit account,
- # but can also be a Asset account with account_type='Income Account' in special circumstances.
+ # income account can be any Credit account,
+ # but can also be a Asset account with account_type='Income Account' in special circumstances.
# Hence the first condition is an "OR"
- return frappe.db.sql("""select tabAccount.name from `tabAccount`
+ return frappe.db.sql("""select tabAccount.name from `tabAccount`
where (tabAccount.report_type = "Profit and Loss"
- or tabAccount.account_type = "Income Account")
- and tabAccount.group_or_ledger="Ledger"
+ or tabAccount.account_type = "Income Account")
+ and tabAccount.group_or_ledger="Ledger"
and tabAccount.docstatus!=2
and ifnull(tabAccount.master_type, "")=""
and ifnull(tabAccount.master_name, "")=""
- and tabAccount.company = '%(company)s'
+ and tabAccount.company = '%(company)s'
and tabAccount.%(key)s LIKE '%(txt)s'
- %(mcond)s""" % {'company': filters['company'], 'key': searchfield,
+ %(mcond)s""" % {'company': filters['company'], 'key': searchfield,
'txt': "%%%s%%" % txt, 'mcond':get_match_cond(doctype)})
@frappe.whitelist()
def make_delivery_note(source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc
-
+
def set_missing_values(source, target):
doc = frappe.get_doc(target)
doc.run_method("onload_post_render")
-
+
def update_item(source_doc, target_doc, source_parent):
target_doc.base_amount = (flt(source_doc.qty) - flt(source_doc.delivered_qty)) * \
flt(source_doc.base_rate)
target_doc.amount = (flt(source_doc.qty) - flt(source_doc.delivered_qty)) * \
flt(source_doc.rate)
target_doc.qty = flt(source_doc.qty) - flt(source_doc.delivered_qty)
-
+
doclist = get_mapped_doc("Sales Invoice", source_name, {
"Sales Invoice": {
- "doctype": "Delivery Note",
+ "doctype": "Delivery Note",
"validation": {
"docstatus": ["=", 1]
}
- },
+ },
"Sales Invoice Item": {
- "doctype": "Delivery Note Item",
+ "doctype": "Delivery Note Item",
"field_map": {
- "name": "prevdoc_detail_docname",
- "parent": "against_sales_invoice",
+ "name": "prevdoc_detail_docname",
+ "parent": "against_sales_invoice",
"serial_no": "serial_no"
},
"postprocess": update_item
- },
+ },
"Sales Taxes and Charges": {
- "doctype": "Sales Taxes and Charges",
+ "doctype": "Sales Taxes and Charges",
"add_if_empty": True
- },
+ },
"Sales Team": {
- "doctype": "Sales Team",
+ "doctype": "Sales Team",
"field_map": {
"incentives": "incentives"
},
"add_if_empty": True
}
}, target_doc, set_missing_values)
-
- return doclist.as_dict()
\ No newline at end of file
+
+ return doclist
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 8baeb76..2a0bc24 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt
import frappe
-import unittest, json
+import unittest, json, copy
from frappe.utils import flt
from erpnext.accounts.utils import get_stock_and_account_difference
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
@@ -14,57 +14,46 @@
w.insert()
w.submit()
return w
-
- def test_double_submission(self):
- w = frappe.copy_doc(test_records[0])
- w.docstatus = '0'
- w.insert()
-
- w2 = frappe.copy_doc(test_records[0])
- w.submit()
-
- w = frappe.get_doc(w2)
- self.assertRaises(frappe.DocstatusTransitionError, w.submit)
-
+
def test_timestamp_change(self):
w = frappe.copy_doc(test_records[0])
- w.docstatus = '0'
+ w.docstatus = 0
w.insert()
- w2 = frappe.copy_doc(w)
-
+ w2 = frappe.get_doc(w.doctype, w.name)
+
import time
time.sleep(1)
w.save()
-
+
import time
time.sleep(1)
self.assertRaises(frappe.TimestampMismatchError, w2.save)
-
+
def test_sales_invoice_calculation_base_currency(self):
si = frappe.copy_doc(test_records[2])
si.insert()
-
+
expected_values = {
- "keys": ["price_list_rate", "discount_percentage", "rate", "amount",
+ "keys": ["price_list_rate", "discount_percentage", "rate", "amount",
"base_price_list_rate", "base_rate", "base_amount"],
"_Test Item Home Desktop 100": [50, 0, 50, 500, 50, 50, 500],
"_Test Item Home Desktop 200": [150, 0, 150, 750, 150, 150, 750],
}
-
+
# check if children are saved
self.assertEquals(len(si.get("entries")),
len(expected_values)-1)
-
+
# check if item values are calculated
for d in si.get("entries"):
for i, k in enumerate(expected_values["keys"]):
self.assertEquals(d.get(k), expected_values[d.item_code][i])
-
+
# check net total
self.assertEquals(si.net_total, 1250)
self.assertEquals(si.net_total_export, 1250)
-
+
# check tax calculation
expected_values = {
"keys": ["tax_amount", "total"],
@@ -77,14 +66,14 @@
"_Test Account VAT - _TC": [156.25, 1807.83],
"_Test Account Discount - _TC": [-180.78, 1627.05]
}
-
+
for d in si.get("other_charges"):
for i, k in enumerate(expected_values["keys"]):
self.assertEquals(d.get(k), expected_values[d.account_head][i])
-
+
self.assertEquals(si.grand_total, 1627.05)
self.assertEquals(si.grand_total_export, 1627.05)
-
+
def test_sales_invoice_calculation_export_currency(self):
si = frappe.copy_doc(test_records[2])
si.currency = "USD"
@@ -94,27 +83,27 @@
si.get("entries")[1].rate = 3
si.get("entries")[1].price_list_rate = 3
si.insert()
-
+
expected_values = {
- "keys": ["price_list_rate", "discount_percentage", "rate", "amount",
+ "keys": ["price_list_rate", "discount_percentage", "rate", "amount",
"base_price_list_rate", "base_rate", "base_amount"],
"_Test Item Home Desktop 100": [1, 0, 1, 10, 50, 50, 500],
"_Test Item Home Desktop 200": [3, 0, 3, 15, 150, 150, 750],
}
-
+
# check if children are saved
self.assertEquals(len(si.get("entries")),
len(expected_values)-1)
-
+
# check if item values are calculated
for d in si.get("entries"):
for i, k in enumerate(expected_values["keys"]):
self.assertEquals(d.get(k), expected_values[d.item_code][i])
-
+
# check net total
self.assertEquals(si.net_total, 1250)
self.assertEquals(si.net_total_export, 25)
-
+
# check tax calculation
expected_values = {
"keys": ["tax_amount", "total"],
@@ -127,11 +116,11 @@
"_Test Account VAT - _TC": [156.25, 1807.83],
"_Test Account Discount - _TC": [-180.78, 1627.05]
}
-
+
for d in si.get("other_charges"):
for i, k in enumerate(expected_values["keys"]):
self.assertEquals(d.get(k), expected_values[d.account_head][i])
-
+
self.assertEquals(si.grand_total, 1627.05)
self.assertEquals(si.grand_total_export, 32.54)
@@ -148,27 +137,27 @@
"row_id": 8,
})
si.insert()
-
+
expected_values = {
- "keys": ["price_list_rate", "discount_percentage", "rate", "amount",
+ "keys": ["price_list_rate", "discount_percentage", "rate", "amount",
"base_price_list_rate", "base_rate", "base_amount"],
"_Test Item Home Desktop 100": [62.5, 0, 62.5, 625.0, 50, 50, 465.37],
"_Test Item Home Desktop 200": [190.66, 0, 190.66, 953.3, 150, 150, 698.08],
}
-
+
# check if children are saved
self.assertEquals(len(si.get("entries")),
len(expected_values)-1)
-
+
# check if item values are calculated
for d in si.get("entries"):
for i, k in enumerate(expected_values["keys"]):
self.assertEquals(d.get(k), expected_values[d.item_code][i])
-
+
# check net total
self.assertEquals(si.net_total, 1163.45)
self.assertEquals(si.net_total_export, 1578.3)
-
+
# check tax calculation
expected_values = {
"keys": ["tax_amount", "tax_amount_after_discount_amount", "total"],
@@ -182,11 +171,11 @@
"_Test Account Discount - _TC": [-180.33, -168.54, 1516.88],
"_Test Account Service Tax - _TC": [-18.03, -16.88, 1500]
}
-
+
for d in si.get("other_charges"):
for i, k in enumerate(expected_values["keys"]):
self.assertEquals(d.get(k), expected_values[d.account_head][i])
-
+
self.assertEquals(si.grand_total, 1500)
self.assertEquals(si.grand_total_export, 1500)
@@ -213,15 +202,15 @@
expected_values = sorted([
[si.debit_to, 1500, 0.0],
- [test_records[3][1]["income_account"], 0.0, 1163.45],
- [test_records[3][3]["account_head"], 0.0, 130.31],
- [test_records[3][4]["account_head"], 0.0, 2.61],
- [test_records[3][5]["account_head"], 0.0, 1.31],
- [test_records[3][6]["account_head"], 0.0, 25.96],
- [test_records[3][7]["account_head"], 0.0, 145.43],
- [test_records[3][8]["account_head"], 0.0, 116.35],
- [test_records[3][9]["account_head"], 0.0, 100],
- [test_records[3][10]["account_head"], 168.54, 0.0],
+ [test_records[3]["entries"][0]["income_account"], 0.0, 1163.45],
+ [test_records[3]["other_charges"][0]["account_head"], 0.0, 130.31],
+ [test_records[3]["other_charges"][1]["account_head"], 0.0, 2.61],
+ [test_records[3]["other_charges"][2]["account_head"], 0.0, 1.31],
+ [test_records[3]["other_charges"][3]["account_head"], 0.0, 25.96],
+ [test_records[3]["other_charges"][4]["account_head"], 0.0, 145.43],
+ [test_records[3]["other_charges"][5]["account_head"], 0.0, 116.35],
+ [test_records[3]["other_charges"][6]["account_head"], 0.0, 100],
+ [test_records[3]["other_charges"][7]["account_head"], 168.54, 0.0],
["_Test Account Service Tax - _TC", 16.88, 0.0],
])
@@ -233,7 +222,7 @@
# cancel
si.cancel()
- gle = frappe.db.sql("""select * from `tabGL Entry`
+ gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
self.assertFalse(gle)
@@ -242,44 +231,44 @@
si = frappe.copy_doc(test_records[2])
for i, tax in enumerate(si.get("other_charges")):
tax.idx = i+1
-
+
si.get("entries")[0].price_list_rate = 62.5
si.get("entries")[0].price_list_rate = 191
- for i in [2, 4, 5, 6, 7, 8]:
+ for i in xrange(6):
si.get("other_charges")[i].included_in_print_rate = 1
-
+
# tax type "Actual" cannot be inclusive
self.assertRaises(frappe.ValidationError, si.insert)
-
+
# taxes above included type 'On Previous Row Total' should also be included
si.get("other_charges")[0].included_in_print_rate = 0
self.assertRaises(frappe.ValidationError, si.insert)
-
+
def test_sales_invoice_calculation_base_currency_with_tax_inclusive_price(self):
# prepare
si = frappe.copy_doc(test_records[3])
si.insert()
-
+
expected_values = {
- "keys": ["price_list_rate", "discount_percentage", "rate", "amount",
+ "keys": ["price_list_rate", "discount_percentage", "rate", "amount",
"base_price_list_rate", "base_rate", "base_amount"],
"_Test Item Home Desktop 100": [62.5, 0, 62.5, 625.0, 50, 50, 499.98],
"_Test Item Home Desktop 200": [190.66, 0, 190.66, 953.3, 150, 150, 750],
}
-
+
# check if children are saved
self.assertEquals(len(si.get("entries")),
len(expected_values)-1)
-
+
# check if item values are calculated
for d in si.get("entries"):
for i, k in enumerate(expected_values["keys"]):
self.assertEquals(d.get(k), expected_values[d.item_code][i])
-
+
# check net total
self.assertEquals(si.net_total, 1249.98)
self.assertEquals(si.net_total_export, 1578.3)
-
+
# check tax calculation
expected_values = {
"keys": ["tax_amount", "total"],
@@ -292,14 +281,14 @@
"_Test Account Shipping Charges - _TC": [100, 1803.31],
"_Test Account Discount - _TC": [-180.33, 1622.98]
}
-
+
for d in si.get("other_charges"):
for i, k in enumerate(expected_values["keys"]):
self.assertEquals(d.get(k), expected_values[d.account_head][i])
-
+
self.assertEquals(si.grand_total, 1622.98)
self.assertEquals(si.grand_total_export, 1622.98)
-
+
def test_sales_invoice_calculation_export_currency_with_tax_inclusive_price(self):
# prepare
si = frappe.copy_doc(test_records[3])
@@ -309,30 +298,29 @@
si.get("entries")[0].discount_percentage = 10
si.get("entries")[1].price_list_rate = 187.5
si.get("entries")[1].discount_percentage = 20
- si.get("other_charges")[5].rate = 5000
-
+ si.get("other_charges")[6].rate = 5000
+
si.insert()
-
+
expected_values = {
- "keys": ["price_list_rate", "discount_percentage", "rate", "amount",
+ "keys": ["price_list_rate", "discount_percentage", "rate", "amount",
"base_price_list_rate", "base_rate", "base_amount"],
"_Test Item Home Desktop 100": [55.56, 10, 50, 500, 2222.11, 1999.9, 19999.04],
"_Test Item Home Desktop 200": [187.5, 20, 150, 750, 7375.66, 5900.53, 29502.66],
}
-
+
# check if children are saved
- self.assertEquals(len(si.get("entries")),
- len(expected_values)-1)
-
+ self.assertEquals(len(si.get("entries")), len(expected_values)-1)
+
# check if item values are calculated
for d in si.get("entries"):
for i, k in enumerate(expected_values["keys"]):
self.assertEquals(d.get(k), expected_values[d.item_code][i])
-
+
# check net total
self.assertEquals(si.net_total, 49501.7)
self.assertEquals(si.net_total_export, 1250)
-
+
# check tax calculation
expected_values = {
"keys": ["tax_amount", "total"],
@@ -345,134 +333,134 @@
"_Test Account Shipping Charges - _TC": [5000, 72450.17],
"_Test Account Discount - _TC": [-7245.01, 65205.16]
}
-
+
for d in si.get("other_charges"):
for i, k in enumerate(expected_values["keys"]):
self.assertEquals(d.get(k), expected_values[d.account_head][i])
-
+
self.assertEquals(si.grand_total, 65205.16)
self.assertEquals(si.grand_total_export, 1304.1)
def test_outstanding(self):
w = self.make()
self.assertEquals(w.outstanding_amount, w.grand_total)
-
+
def test_payment(self):
frappe.db.sql("""delete from `tabGL Entry`""")
w = self.make()
-
+
from erpnext.accounts.doctype.journal_voucher.test_journal_voucher \
import test_records as jv_test_records
-
+
jv = frappe.get_doc(frappe.copy_doc(jv_test_records[0]))
jv.get("entries")[0].against_invoice = w.name
jv.insert()
jv.submit()
-
+
self.assertEquals(frappe.db.get_value("Sales Invoice", w.name, "outstanding_amount"),
161.8)
-
+
jv.cancel()
self.assertEquals(frappe.db.get_value("Sales Invoice", w.name, "outstanding_amount"),
561.8)
-
+
def test_time_log_batch(self):
tlb = frappe.get_doc("Time Log Batch", "_T-Time Log Batch-00001")
tlb.submit()
-
+
si = frappe.get_doc(frappe.copy_doc(test_records[0]))
si.get("entries")[0].time_log_batch = "_T-Time Log Batch-00001"
si.insert()
si.submit()
-
+
self.assertEquals(frappe.db.get_value("Time Log Batch", "_T-Time Log Batch-00001",
"status"), "Billed")
- self.assertEquals(frappe.db.get_value("Time Log", "_T-Time Log-00001", "status"),
+ self.assertEquals(frappe.db.get_value("Time Log", "_T-Time Log-00001", "status"),
"Billed")
si.cancel()
- self.assertEquals(frappe.db.get_value("Time Log Batch", "_T-Time Log Batch-00001",
+ self.assertEquals(frappe.db.get_value("Time Log Batch", "_T-Time Log Batch-00001",
"status"), "Submitted")
- self.assertEquals(frappe.db.get_value("Time Log", "_T-Time Log-00001", "status"),
+ self.assertEquals(frappe.db.get_value("Time Log", "_T-Time Log-00001", "status"),
"Batched for Billing")
-
+
def test_sales_invoice_gl_entry_without_aii(self):
self.clear_stock_account_balance()
set_perpetual_inventory(0)
si = frappe.copy_doc(test_records[1])
si.insert()
si.submit()
-
+
gl_entries = frappe.db.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc""", si.name, as_dict=1)
-
+
self.assertTrue(gl_entries)
-
+
expected_values = sorted([
[si.debit_to, 630.0, 0.0],
- [test_records[1][1]["income_account"], 0.0, 500.0],
- [test_records[1][2]["account_head"], 0.0, 80.0],
- [test_records[1][3]["account_head"], 0.0, 50.0],
+ [test_records[1]["entries"][0]["income_account"], 0.0, 500.0],
+ [test_records[1]["other_charges"][0]["account_head"], 0.0, 80.0],
+ [test_records[1]["other_charges"][1]["account_head"], 0.0, 50.0],
])
-
+
for i, gle in enumerate(gl_entries):
self.assertEquals(expected_values[i][0], gle.account)
self.assertEquals(expected_values[i][1], gle.debit)
self.assertEquals(expected_values[i][2], gle.credit)
-
+
# cancel
si.cancel()
-
- gle = frappe.db.sql("""select * from `tabGL Entry`
+
+ gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
-
+
self.assertFalse(gle)
-
+
def test_pos_gl_entry_with_aii(self):
self.clear_stock_account_balance()
set_perpetual_inventory()
-
+
self._insert_purchase_receipt()
self._insert_pos_settings()
-
- pos = frappe.copy_doc(test_records[1])
- pos[0]["is_pos"] = 1
- pos[0]["update_stock"] = 1
- pos[0]["posting_time"] = "12:05"
- pos[0]["cash_bank_account"] = "_Test Account Bank Account - _TC"
- pos[0]["paid_amount"] = 600.0
+
+ pos = copy.deepcopy(test_records[1])
+ pos["is_pos"] = 1
+ pos["update_stock"] = 1
+ pos["posting_time"] = "12:05"
+ pos["cash_bank_account"] = "_Test Account Bank Account - _TC"
+ pos["paid_amount"] = 600.0
si = frappe.copy_doc(pos)
si.insert()
si.submit()
-
+
# check stock ledger entries
- sle = frappe.db.sql("""select * from `tabStock Ledger Entry`
- where voucher_type = 'Sales Invoice' and voucher_no = %s""",
+ sle = frappe.db.sql("""select * from `tabStock Ledger Entry`
+ where voucher_type = 'Sales Invoice' and voucher_no = %s""",
si.name, as_dict=1)[0]
self.assertTrue(sle)
- self.assertEquals([sle.item_code, sle.warehouse, sle.actual_qty],
+ self.assertEquals([sle.item_code, sle.warehouse, sle.actual_qty],
["_Test Item", "_Test Warehouse - _TC", -1.0])
-
+
# check gl entries
gl_entries = frappe.db.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc, debit asc""", si.name, as_dict=1)
self.assertTrue(gl_entries)
-
+
stock_in_hand = frappe.db.get_value("Account", {"master_name": "_Test Warehouse - _TC"})
-
+
expected_gl_entries = sorted([
[si.debit_to, 630.0, 0.0],
- [pos[1]["income_account"], 0.0, 500.0],
- [pos[2]["account_head"], 0.0, 80.0],
- [pos[3]["account_head"], 0.0, 50.0],
+ [pos["entries"][0]["income_account"], 0.0, 500.0],
+ [pos["other_charges"][0]["account_head"], 0.0, 80.0],
+ [pos["other_charges"][1]["account_head"], 0.0, 50.0],
[stock_in_hand, 0.0, 75.0],
- [pos[1]["expense_account"], 75.0, 0.0],
+ [pos["entries"][0]["expense_account"], 75.0, 0.0],
[si.debit_to, 0.0, 600.0],
["_Test Account Bank Account - _TC", 600.0, 0.0]
])
@@ -480,57 +468,57 @@
self.assertEquals(expected_gl_entries[i][0], gle.account)
self.assertEquals(expected_gl_entries[i][1], gle.debit)
self.assertEquals(expected_gl_entries[i][2], gle.credit)
-
+
si.cancel()
- gle = frappe.db.sql("""select * from `tabGL Entry`
+ gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
-
+
self.assertFalse(gle)
-
+
self.assertFalse(get_stock_and_account_difference([stock_in_hand]))
-
+
set_perpetual_inventory(0)
-
+
def test_si_gl_entry_with_aii_and_update_stock_with_warehouse_but_no_account(self):
self.clear_stock_account_balance()
set_perpetual_inventory()
frappe.delete_doc("Account", "_Test Warehouse No Account - _TC")
-
+
# insert purchase receipt
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import test_records \
as pr_test_records
pr = frappe.copy_doc(pr_test_records[0])
pr.naming_series = "_T-Purchase Receipt-"
- pr.get("entries")[0].warehouse = "_Test Warehouse No Account - _TC"
+ pr.get("purchase_receipt_details")[0].warehouse = "_Test Warehouse No Account - _TC"
pr.insert()
pr.submit()
-
- si_doc = frappe.copy_doc(test_records[1])
+
+ si_doc = copy.deepcopy(test_records[1])
si_doc["update_stock"] = 1
si_doc["posting_time"] = "12:05"
- si_doc.get("entries")["warehouse"] = "_Test Warehouse No Account - _TC"
+ si_doc.get("entries")[0]["warehouse"] = "_Test Warehouse No Account - _TC"
si = frappe.copy_doc(si_doc)
si.insert()
si.submit()
-
+
# check stock ledger entries
- sle = frappe.db.sql("""select * from `tabStock Ledger Entry`
- where voucher_type = 'Sales Invoice' and voucher_no = %s""",
+ sle = frappe.db.sql("""select * from `tabStock Ledger Entry`
+ where voucher_type = 'Sales Invoice' and voucher_no = %s""",
si.name, as_dict=1)[0]
self.assertTrue(sle)
- self.assertEquals([sle.item_code, sle.warehouse, sle.actual_qty],
+ self.assertEquals([sle.item_code, sle.warehouse, sle.actual_qty],
["_Test Item", "_Test Warehouse No Account - _TC", -1.0])
-
+
# check gl entries
gl_entries = frappe.db.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc, debit asc""", si.name, as_dict=1)
self.assertTrue(gl_entries)
-
+
expected_gl_entries = sorted([
[si.debit_to, 630.0, 0.0],
- [si_doc.get("entries")["income_account"], 0.0, 500.0],
+ [si_doc.get("entries")[0]["income_account"], 0.0, 500.0],
[si_doc.get("other_charges")[0]["account_head"], 0.0, 80.0],
[si_doc.get("other_charges")[1]["account_head"], 0.0, 50.0],
])
@@ -538,69 +526,67 @@
self.assertEquals(expected_gl_entries[i][0], gle.account)
self.assertEquals(expected_gl_entries[i][1], gle.debit)
self.assertEquals(expected_gl_entries[i][2], gle.credit)
-
+
si.cancel()
- gle = frappe.db.sql("""select * from `tabGL Entry`
+ gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
-
+
self.assertFalse(gle)
set_perpetual_inventory(0)
-
+
def test_sales_invoice_gl_entry_with_aii_no_item_code(self):
self.clear_stock_account_balance()
set_perpetual_inventory()
-
- si_copy = frappe.copy_doc(test_records[1])
- si_copy[1]["item_code"] = None
- si = frappe.get_doc(si_copy)
+
+ si = frappe.get_doc(test_records[1])
+ si.get("entries")[0].item_code = None
si.insert()
si.submit()
-
+
gl_entries = frappe.db.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc""", si.name, as_dict=1)
self.assertTrue(gl_entries)
-
+
expected_values = sorted([
[si.debit_to, 630.0, 0.0],
- [test_records[1][1]["income_account"], 0.0, 500.0],
- [test_records[1][2]["account_head"], 0.0, 80.0],
- [test_records[1][3]["account_head"], 0.0, 50.0],
+ [test_records[1]["entries"][0]["income_account"], 0.0, 500.0],
+ [test_records[1]["other_charges"][0]["account_head"], 0.0, 80.0],
+ [test_records[1]["other_charges"][1]["account_head"], 0.0, 50.0],
])
for i, gle in enumerate(gl_entries):
self.assertEquals(expected_values[i][0], gle.account)
self.assertEquals(expected_values[i][1], gle.debit)
self.assertEquals(expected_values[i][2], gle.credit)
-
+
set_perpetual_inventory(0)
-
+
def test_sales_invoice_gl_entry_with_aii_non_stock_item(self):
self.clear_stock_account_balance()
set_perpetual_inventory()
- si_copy = frappe.copy_doc(test_records[1])
- si_copy[1]["item_code"] = "_Test Non Stock Item"
- si = frappe.get_doc(si_copy)
+ si = frappe.get_doc(test_records[1])
+ si.get("entries")[0].item_code = "_Test Non Stock Item"
si.insert()
si.submit()
-
+
gl_entries = frappe.db.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc""", si.name, as_dict=1)
self.assertTrue(gl_entries)
-
+
expected_values = sorted([
[si.debit_to, 630.0, 0.0],
- [test_records[1][1]["income_account"], 0.0, 500.0],
- [test_records[1][2]["account_head"], 0.0, 80.0],
- [test_records[1][3]["account_head"], 0.0, 50.0],
+ [test_records[1]["entries"][0]["income_account"], 0.0, 500.0],
+ [test_records[1]["other_charges"][0]["account_head"], 0.0, 80.0],
+ [test_records[1]["other_charges"][1]["account_head"], 0.0, 50.0],
])
for i, gle in enumerate(gl_entries):
self.assertEquals(expected_values[i][0], gle.account)
self.assertEquals(expected_values[i][1], gle.debit)
self.assertEquals(expected_values[i][2], gle.credit)
-
+
set_perpetual_inventory(0)
-
+
def _insert_purchase_receipt(self):
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import test_records \
as pr_test_records
@@ -608,7 +594,7 @@
pr.naming_series = "_T-Purchase Receipt-"
pr.insert()
pr.submit()
-
+
def _insert_delivery_note(self):
from erpnext.stock.doctype.delivery_note.test_delivery_note import test_records \
as dn_test_records
@@ -617,23 +603,23 @@
dn.insert()
dn.submit()
return dn
-
+
def _insert_pos_settings(self):
from erpnext.accounts.doctype.pos_setting.test_pos_setting \
import test_records as pos_setting_test_records
frappe.db.sql("""delete from `tabPOS Setting`""")
-
+
ps = frappe.copy_doc(pos_setting_test_records[0])
ps.insert()
-
+
def test_sales_invoice_with_advance(self):
from erpnext.accounts.doctype.journal_voucher.test_journal_voucher \
import test_records as jv_test_records
-
+
jv = frappe.copy_doc(jv_test_records[0])
jv.insert()
jv.submit()
-
+
si = frappe.copy_doc(test_records[0])
si.append("advance_adjustment_details", {
"doctype": "Sales Invoice Advance",
@@ -646,20 +632,20 @@
si.insert()
si.submit()
si.load_from_db()
-
+
self.assertTrue(frappe.db.sql("""select name from `tabJournal Voucher Detail`
where against_invoice=%s""", si.name))
-
+
self.assertTrue(frappe.db.sql("""select name from `tabJournal Voucher Detail`
where against_invoice=%s and credit=300""", si.name))
-
+
self.assertEqual(si.outstanding_amount, 261.8)
-
+
si.cancel()
-
+
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Voucher Detail`
where against_invoice=%s""", si.name))
-
+
def test_recurring_invoice(self):
from frappe.utils import get_first_day, get_last_day, add_to_date, nowdate, getdate
from erpnext.accounts.utils import get_fiscal_year
@@ -675,13 +661,13 @@
"invoice_period_from_date": get_first_day(today),
"invoice_period_to_date": get_last_day(today)
})
-
+
# monthly
si1 = frappe.copy_doc(base_si)
si1.insert()
si1.submit()
self._test_recurring_invoice(si1, True)
-
+
# monthly without a first and last day period
si2 = frappe.copy_doc(base_si)
si2.update({
@@ -691,7 +677,7 @@
si2.insert()
si2.submit()
self._test_recurring_invoice(si2, False)
-
+
# quarterly
si3 = frappe.copy_doc(base_si)
si3.update({
@@ -702,7 +688,7 @@
si3.insert()
si3.submit()
self._test_recurring_invoice(si3, True)
-
+
# quarterly without a first and last day period
si4 = frappe.copy_doc(base_si)
si4.update({
@@ -713,7 +699,7 @@
si4.insert()
si4.submit()
self._test_recurring_invoice(si4, False)
-
+
# yearly
si5 = frappe.copy_doc(base_si)
si5.update({
@@ -724,7 +710,7 @@
si5.insert()
si5.submit()
self._test_recurring_invoice(si5, True)
-
+
# yearly without a first and last day period
si6 = frappe.copy_doc(base_si)
si6.update({
@@ -735,7 +721,7 @@
si6.insert()
si6.submit()
self._test_recurring_invoice(si6, False)
-
+
# change posting date but keep recuring day to be today
si7 = frappe.copy_doc(base_si)
si7.update({
@@ -743,7 +729,7 @@
})
si7.insert()
si7.submit()
-
+
# setting so that _test function works
si7.posting_date = today
self._test_recurring_invoice(si7, True)
@@ -752,52 +738,52 @@
from frappe.utils import add_months, get_last_day
from erpnext.accounts.doctype.sales_invoice.sales_invoice \
import manage_recurring_invoices, get_next_date
-
+
no_of_months = ({"Monthly": 1, "Quarterly": 3, "Yearly": 12})[base_si.recurring_type]
-
+
def _test(i):
self.assertEquals(i+1, frappe.db.sql("""select count(*) from `tabSales Invoice`
where recurring_id=%s and docstatus=1""", base_si.recurring_id)[0][0])
-
- next_date = get_next_date(base_si.posting_date, no_of_months,
+
+ next_date = get_next_date(base_si.posting_date, no_of_months,
base_si.repeat_on_day_of_month)
manage_recurring_invoices(next_date=next_date, commit=False)
-
+
recurred_invoices = frappe.db.sql("""select name from `tabSales Invoice`
where recurring_id=%s and docstatus=1 order by name desc""",
base_si.recurring_id)
-
+
self.assertEquals(i+2, len(recurred_invoices))
-
+
new_si = frappe.get_doc("Sales Invoice", recurred_invoices[0][0])
-
+
for fieldname in ["convert_into_recurring_invoice", "recurring_type",
"repeat_on_day_of_month", "notification_email_address"]:
self.assertEquals(base_si.get(fieldname),
new_si.get(fieldname))
self.assertEquals(new_si.posting_date, unicode(next_date))
-
+
self.assertEquals(new_si.invoice_period_from_date,
unicode(add_months(base_si.invoice_period_from_date, no_of_months)))
-
+
if first_and_last_day:
- self.assertEquals(new_si.invoice_period_to_date,
+ self.assertEquals(new_si.invoice_period_to_date,
unicode(get_last_day(add_months(base_si.invoice_period_to_date,
no_of_months))))
else:
- self.assertEquals(new_si.invoice_period_to_date,
+ self.assertEquals(new_si.invoice_period_to_date,
unicode(add_months(base_si.invoice_period_to_date, no_of_months)))
-
-
+
+
return new_si
-
+
# if yearly, test 1 repetition, else test 5 repetitions
count = 1 if (no_of_months == 12) else 5
for i in xrange(count):
base_si = _test(i)
-
+
def clear_stock_account_balance(self):
frappe.db.sql("delete from `tabStock Ledger Entry`")
frappe.db.sql("delete from tabBin")
@@ -806,10 +792,10 @@
def test_serialized(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
-
+
se = make_serialized_item()
- serial_nos = get_serial_nos(se.get("entries")[0].serial_no)
-
+ serial_nos = get_serial_nos(se.get("mtn_details")[0].serial_no)
+
si = frappe.copy_doc(test_records[0])
si.update_stock = 1
si.get("entries")[0].item_code = "_Test Serialized Item With Series"
@@ -817,14 +803,14 @@
si.get("entries")[0].serial_no = serial_nos[0]
si.insert()
si.submit()
-
+
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "status"), "Delivered")
self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"))
- self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0],
+ self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0],
"delivery_document_no"), si.name)
-
+
return si
-
+
def test_serialized_cancel(self):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
si = self.test_serialized()
@@ -834,20 +820,20 @@
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "status"), "Available")
self.assertEquals(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"), "_Test Warehouse - _TC")
- self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0],
+ self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0],
"delivery_document_no"))
def test_serialize_status(self):
from erpnext.stock.doctype.serial_no.serial_no import SerialNoStatusError, get_serial_nos
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
-
+
se = make_serialized_item()
- serial_nos = get_serial_nos(se.get("entries")[0].serial_no)
-
+ serial_nos = get_serial_nos(se.get("mtn_details")[0].serial_no)
+
sr = frappe.get_doc("Serial No", serial_nos[0])
sr.status = "Not Available"
sr.save()
-
+
si = frappe.copy_doc(test_records[0])
si.update_stock = 1
si.get("entries")[0].item_code = "_Test Serialized Item With Series"
@@ -858,5 +844,4 @@
self.assertRaises(SerialNoStatusError, si.submit)
test_dependencies = ["Journal Voucher", "POS Setting", "Contact", "Address"]
-
-test_records = frappe.get_test_records('Sales Invoice')
\ No newline at end of file
+test_records = frappe.get_test_records('Sales Invoice')
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 25bdfa9..720a1d5 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -8,7 +8,7 @@
from frappe import msgprint
-
+
from erpnext.controllers.buying_controller import BuyingController
class PurchaseOrder(BuyingController):
tname = 'Purchase Order Item'
@@ -24,15 +24,15 @@
'source_field': 'qty',
'percent_join_field': 'prevdoc_docname',
}]
-
+
def validate(self):
super(PurchaseOrder, self).validate()
-
+
if not self.status:
self.status = "Draft"
from erpnext.utilities import validate_status
- validate_status(self.status, ["Draft", "Submitted", "Stopped",
+ validate_status(self.status, ["Draft", "Submitted", "Stopped",
"Cancelled"])
pc_obj = frappe.get_doc('Purchase Common')
@@ -45,7 +45,7 @@
self.validate_with_previous_doc()
self.validate_for_subcontracting()
self.update_raw_materials_supplied("po_raw_material_details")
-
+
def validate_with_previous_doc(self):
super(PurchaseOrder, self).validate_with_previous_doc(self.tname, {
"Supplier Quotation": {
@@ -54,7 +54,7 @@
},
"Supplier Quotation Item": {
"ref_dn_field": "supplier_quotation_item",
- "compare_fields": [["rate", "="], ["project_name", "="], ["item_code", "="],
+ "compare_fields": [["rate", "="], ["project_name", "="], ["item_code", "="],
["uom", "="]],
"is_child_table": True
}
@@ -65,11 +65,11 @@
if d.prevdoc_detail_docname and not d.schedule_date:
d.schedule_date = frappe.db.get_value("Material Request Item",
d.prevdoc_detail_docname, "schedule_date")
-
+
def get_last_purchase_rate(self):
frappe.get_doc('Purchase Common').get_last_purchase_rate(self)
- # Check for Stopped status
+ # Check for Stopped status
def check_for_stopped_status(self, pc_obj):
check_list =[]
for d in self.get('po_details'):
@@ -77,7 +77,7 @@
check_list.append(d.prevdoc_docname)
pc_obj.check_for_stopped_status( d.prevdoc_doctype, d.prevdoc_docname)
-
+
def update_bin(self, is_submit, is_stopped = 0):
from erpnext.stock.utils import update_bin
pc_obj = frappe.get_doc('Purchase Common')
@@ -87,29 +87,29 @@
# this happens when item is changed from non-stock to stock item
if not d.warehouse:
continue
-
+
ind_qty, po_qty = 0, flt(d.qty) * flt(d.conversion_factor)
if is_stopped:
po_qty = flt(d.qty) > flt(d.received_qty) and \
- flt( flt(flt(d.qty) - flt(d.received_qty))*flt(d.conversion_factor)) or 0
-
+ flt( flt(flt(d.qty) - flt(d.received_qty))*flt(d.conversion_factor)) or 0
+
# No updates in Material Request on Stop / Unstop
if cstr(d.prevdoc_doctype) == 'Material Request' and not is_stopped:
- # get qty and pending_qty of prevdoc
+ # get qty and pending_qty of prevdoc
curr_ref_qty = pc_obj.get_qty(d.doctype, 'prevdoc_detail_docname',
- d.prevdoc_detail_docname, 'Material Request Item',
+ d.prevdoc_detail_docname, 'Material Request Item',
'Material Request - Purchase Order', self.name)
max_qty, qty, curr_qty = flt(curr_ref_qty.split('~~~')[1]), \
flt(curr_ref_qty.split('~~~')[0]), 0
-
+
if flt(qty) + flt(po_qty) > flt(max_qty):
curr_qty = flt(max_qty) - flt(qty)
- # special case as there is no restriction
- # for Material Request - Purchase Order
+ # special case as there is no restriction
+ # for Material Request - Purchase Order
curr_qty = curr_qty > 0 and curr_qty or 0
else:
curr_qty = flt(po_qty)
-
+
ind_qty = -flt(curr_qty)
# Update ordered_qty and indented_qty in bin
@@ -121,12 +121,12 @@
"posting_date": self.transaction_date
}
update_bin(args)
-
+
def check_modified_date(self):
- mod_db = frappe.db.sql("select modified from `tabPurchase Order` where name = %s",
+ mod_db = frappe.db.sql("select modified from `tabPurchase Order` where name = %s",
self.name)
date_diff = frappe.db.sql("select TIMEDIFF('%s', '%s')" % ( mod_db[0][0],cstr(self.modified)))
-
+
if date_diff and date_diff[0][0]:
msgprint(cstr(self.doctype) +" => "+ cstr(self.name) +" has been modified. Please Refresh. ")
raise Exception
@@ -144,28 +144,28 @@
def on_submit(self):
purchase_controller = frappe.get_doc("Purchase Common")
-
+
self.update_prevdoc_status()
self.update_bin(is_submit = 1, is_stopped = 0)
-
- frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
+
+ frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
self.company, self.grand_total)
-
+
purchase_controller.update_last_purchase_rate(self, is_submit = 1)
-
+
frappe.db.set(self,'status','Submitted')
-
+
def on_cancel(self):
- pc_obj = frappe.get_doc(dt = 'Purchase Common')
+ pc_obj = frappe.get_doc(dt = 'Purchase Common')
self.check_for_stopped_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')
# 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
- where t1.name = t2.parent and t2.purchase_order = %s and t1.docstatus = 1""",
+ submitted = frappe.db.sql("""select t1.name
+ from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2
+ where t1.name = t2.parent and t2.purchase_order = %s and t1.docstatus = 1""",
self.name)
if submitted:
msgprint("Purchase Invoice : " + cstr(submitted[0][0]) + " has already been submitted !")
@@ -175,14 +175,14 @@
self.update_prevdoc_status()
self.update_bin( is_submit = 0, is_stopped = 0)
pc_obj.update_last_purchase_rate(self, is_submit = 0)
-
+
def on_update(self):
pass
-
+
@frappe.whitelist()
def make_purchase_receipt(source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc
-
+
def set_missing_values(source, target):
doc = frappe.get_doc(target)
doc.run_method("set_missing_values")
@@ -193,35 +193,35 @@
target.amount = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.rate)
target.base_amount = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.base_rate)
- doclist = get_mapped_doc("Purchase Order", source_name, {
+ doc = get_mapped_doc("Purchase Order", source_name, {
"Purchase Order": {
- "doctype": "Purchase Receipt",
+ "doctype": "Purchase Receipt",
"validation": {
"docstatus": ["=", 1],
}
- },
+ },
"Purchase Order Item": {
- "doctype": "Purchase Receipt Item",
+ "doctype": "Purchase Receipt Item",
"field_map": {
- "name": "prevdoc_detail_docname",
- "parent": "prevdoc_docname",
- "parenttype": "prevdoc_doctype",
+ "name": "prevdoc_detail_docname",
+ "parent": "prevdoc_docname",
+ "parenttype": "prevdoc_doctype",
},
"postprocess": update_item,
"condition": lambda doc: doc.received_qty < doc.qty
- },
+ },
"Purchase Taxes and Charges": {
- "doctype": "Purchase Taxes and Charges",
+ "doctype": "Purchase Taxes and Charges",
"add_if_empty": True
}
}, target_doc, set_missing_values)
- return doclist.as_dict()
-
+ return doc
+
@frappe.whitelist()
def make_purchase_invoice(source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc
-
+
def set_missing_values(source, target):
doc = frappe.get_doc(target)
doc.run_method("set_missing_values")
@@ -232,26 +232,26 @@
if flt(obj.base_rate):
target.qty = target.base_amount / flt(obj.base_rate)
- doclist = get_mapped_doc("Purchase Order", source_name, {
+ doc = get_mapped_doc("Purchase Order", source_name, {
"Purchase Order": {
- "doctype": "Purchase Invoice",
+ "doctype": "Purchase Invoice",
"validation": {
"docstatus": ["=", 1],
}
- },
+ },
"Purchase Order Item": {
- "doctype": "Purchase Invoice Item",
+ "doctype": "Purchase Invoice Item",
"field_map": {
- "name": "po_detail",
- "parent": "purchase_order",
+ "name": "po_detail",
+ "parent": "purchase_order",
},
"postprocess": update_item,
- "condition": lambda doc: doc.base_amount==0 or doc.billed_amt < doc.amount
- },
+ "condition": lambda doc: doc.base_amount==0 or doc.billed_amt < doc.amount
+ },
"Purchase Taxes and Charges": {
- "doctype": "Purchase Taxes and Charges",
+ "doctype": "Purchase Taxes and Charges",
"add_if_empty": True
}
}, target_doc, set_missing_values)
- return doclist.as_dict()
\ No newline at end of file
+ return doc
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index f8b03f0..d1d183a 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -8,90 +8,87 @@
from frappe.utils import flt
class TestPurchaseOrder(unittest.TestCase):
- def test_make_purchase_receipt(self):
+ def test_make_purchase_receipt(self):
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
po = frappe.copy_doc(test_records[0]).insert()
- self.assertRaises(frappe.ValidationError, make_purchase_receipt,
+ self.assertRaises(frappe.ValidationError, make_purchase_receipt,
po.name)
po = frappe.get_doc("Purchase Order", po.name)
po.submit()
-
+
pr = make_purchase_receipt(po.name)
- pr[0]["supplier_warehouse"] = "_Test Warehouse 1 - _TC"
- pr[0]["posting_date"] = "2013-05-12"
- self.assertEquals(pr[0]["doctype"], "Purchase Receipt")
- self.assertEquals(len(pr), len(test_records[0]))
-
- pr[0]["naming_series"] = "_T-Purchase Receipt-"
- pr_doc = frappe.get_doc(pr)
- pr_doc.insert()
-
+ pr.supplier_warehouse = "_Test Warehouse 1 - _TC"
+ pr.posting_date = "2013-05-12"
+ self.assertEquals(pr.doctype, "Purchase Receipt")
+ self.assertEquals(len(pr.get("purchase_receipt_details")), len(test_records[0]["po_details"]))
+
+ pr.naming_series = "_T-Purchase Receipt-"
+ frappe.get_doc(pr).insert()
+
def test_ordered_qty(self):
frappe.db.sql("delete from tabBin")
-
+
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
po = frappe.copy_doc(test_records[0]).insert()
- self.assertRaises(frappe.ValidationError, make_purchase_receipt,
+ self.assertRaises(frappe.ValidationError, make_purchase_receipt,
po.name)
po = frappe.get_doc("Purchase Order", po.name)
po.is_subcontracted = "No"
po.get("po_details")[0].item_code = "_Test Item"
po.submit()
-
- self.assertEquals(frappe.db.get_value("Bin", {"item_code": "_Test Item",
+
+ self.assertEquals(frappe.db.get_value("Bin", {"item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC"}, "ordered_qty"), 10)
-
+
pr = make_purchase_receipt(po.name)
-
- self.assertEquals(pr[0]["doctype"], "Purchase Receipt")
- self.assertEquals(len(pr), len(test_records[0]))
- pr[0]["posting_date"] = "2013-05-12"
- pr[0]["naming_series"] = "_T-Purchase Receipt-"
- pr[1]["qty"] = 4.0
- pr_doc = frappe.get_doc(pr)
- pr_doc.insert()
- pr_doc.submit()
-
- self.assertEquals(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item",
+
+ self.assertEquals(pr.doctype, "Purchase Receipt")
+ self.assertEquals(len(pr.get("purchase_receipt_details", [])), len(test_records[0]["po_details"]))
+ pr.posting_date = "2013-05-12"
+ pr.naming_series = "_T-Purchase Receipt-"
+ pr.purchase_receipt_details[0].qty = 4.0
+ pr.insert()
+ pr.submit()
+
+ self.assertEquals(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC"}, "ordered_qty")), 6.0)
-
+
frappe.db.set_value('Item', '_Test Item', 'tolerance', 50)
-
+
pr1 = make_purchase_receipt(po.name)
- pr1[0]["naming_series"] = "_T-Purchase Receipt-"
- pr1[0]["posting_date"] = "2013-05-12"
- pr1[1]["qty"] = 8
- pr1_doc = frappe.get_doc(pr1)
- pr1_doc.insert()
- pr1_doc.submit()
-
- self.assertEquals(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item",
+ pr1.naming_series = "_T-Purchase Receipt-"
+ pr1.posting_date = "2013-05-12"
+ pr1.get("purchase_receipt_details")[0].qty = 8
+ pr1.insert()
+ pr1.submit()
+
+ self.assertEquals(flt(frappe.db.get_value("Bin", {"item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC"}, "ordered_qty")), 0.0)
-
+
def test_make_purchase_invoice(self):
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_invoice
po = frappe.copy_doc(test_records[0]).insert()
- self.assertRaises(frappe.ValidationError, make_purchase_invoice,
+ self.assertRaises(frappe.ValidationError, make_purchase_invoice,
po.name)
po = frappe.get_doc("Purchase Order", po.name)
po.submit()
pi = make_purchase_invoice(po.name)
-
- self.assertEquals(pi[0]["doctype"], "Purchase Invoice")
- self.assertEquals(len(pi), len(test_records[0]))
- pi[0]["posting_date"] = "2013-05-12"
- pi[0]["bill_no"] = "NA"
+
+ self.assertEquals(pi.doctype, "Purchase Invoice")
+ self.assertEquals(len(pi.get("entries", [])), len(test_records[0]["po_details"]))
+ pi.posting_date = "2013-05-12"
+ pi.bill_no = "NA"
frappe.get_doc(pi).insert()
-
+
def test_subcontracting(self):
po = frappe.copy_doc(test_records[0])
po.insert()
@@ -113,4 +110,4 @@
test_dependencies = ["BOM"]
-test_records = frappe.get_test_records('Purchase Order')
\ No newline at end of file
+test_records = frappe.get_test_records('Purchase Order')
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
index e937735..fa48488 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
@@ -88,4 +88,4 @@
},
}, target_doc, set_missing_values)
- return doclist.as_dict()
\ No newline at end of file
+ return doclist
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py
index 708b5e7..3f22fd5 100644
--- a/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py
+++ b/erpnext/buying/doctype/supplier_quotation/test_supplier_quotation.py
@@ -13,22 +13,22 @@
sq = frappe.copy_doc(test_records[0]).insert()
- self.assertRaises(frappe.ValidationError, make_purchase_order,
+ self.assertRaises(frappe.ValidationError, make_purchase_order,
sq.name)
sq = frappe.get_doc("Supplier Quotation", sq.name)
sq.submit()
po = make_purchase_order(sq.name)
-
- self.assertEquals(po[0]["doctype"], "Purchase Order")
- self.assertEquals(len(po), len(sq))
-
- po[0]["naming_series"] = "_T-Purchase Order-"
- for doc in po:
+ self.assertEquals(po.doctype, "Purchase Order")
+ self.assertEquals(len(po.get("po_details")), len(sq.get("quotation_items")))
+
+ po.naming_series = "_T-Purchase Order-"
+
+ for doc in po.get("po_details"):
if doc.get("item_code"):
- doc["schedule_date"] = "2013-04-12"
+ doc.set("schedule_date", "2013-04-12")
- frappe.get_doc(po).insert()
-
-test_records = frappe.get_test_records('Supplier Quotation')
\ No newline at end of file
+ po.insert()
+
+test_records = frappe.get_test_records('Supplier Quotation')
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index ffe6a6a..ec6fe1f 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -18,16 +18,16 @@
self.calculate_taxes_and_totals()
self.validate_value("grand_total", ">=", 0)
self.set_total_in_words()
-
+
self.validate_for_freezed_account()
-
+
def set_missing_values(self, for_validate=False):
for fieldname in ["posting_date", "transaction_date"]:
if not self.get(fieldname) and self.meta.get_field(fieldname):
self.set(fieldname, today())
if not self.fiscal_year:
self.fiscal_year = get_fiscal_year(self.get(fieldname))[0]
-
+
def validate_date_with_fiscal_year(self):
if self.meta.get_field("fiscal_year") :
date_field = ""
@@ -35,40 +35,40 @@
date_field = "posting_date"
elif self.meta.get_field("transaction_date"):
date_field = "transaction_date"
-
+
if date_field and self.get(date_field):
- validate_fiscal_year(self.get(date_field), self.fiscal_year,
+ validate_fiscal_year(self.get(date_field), self.fiscal_year,
label=self.meta.get_label(date_field))
-
+
def validate_for_freezed_account(self):
for fieldname in ["customer", "supplier"]:
if self.meta.get_field(fieldname) and self.get(fieldname):
- accounts = frappe.db.get_values("Account",
- {"master_type": fieldname.title(), "master_name": self.get(fieldname),
+ accounts = frappe.db.get_values("Account",
+ {"master_type": fieldname.title(), "master_name": self.get(fieldname),
"company": self.company}, "name")
if accounts:
from erpnext.accounts.doctype.gl_entry.gl_entry import validate_frozen_account
- for account in accounts:
+ for account in accounts:
validate_frozen_account(account[0])
-
+
def set_price_list_currency(self, buying_or_selling):
if self.meta.get_field("currency"):
company_currency = get_company_currency(self.company)
-
+
# price list part
fieldname = "selling_price_list" if buying_or_selling.lower() == "selling" \
else "buying_price_list"
if self.meta.get_field(fieldname) and self.get(fieldname):
self.price_list_currency = frappe.db.get_value("Price List",
self.get(fieldname), "currency")
-
+
if self.price_list_currency == company_currency:
self.plc_conversion_rate = 1.0
elif not self.plc_conversion_rate:
self.plc_conversion_rate = self.get_exchange_rate(
self.price_list_currency, company_currency)
-
+
# currency
if not self.currency:
self.currency = self.price_list_currency
@@ -96,44 +96,44 @@
if item.meta.get_field(fieldname) and \
item.get(fieldname) is None and value is not None:
item.set(fieldname, value)
-
+
def set_taxes(self, tax_parentfield, tax_master_field):
if not self.meta.get_field(tax_parentfield):
return
-
+
tax_master_doctype = self.meta.get_field(tax_master_field).options
-
+
if not self.get(tax_parentfield):
if not self.get(tax_master_field):
# get the default tax master
self.set(tax_master_field, frappe.db.get_value(tax_master_doctype, {"is_default": 1}))
-
+
self.append_taxes_from_master(tax_parentfield, tax_master_field, tax_master_doctype)
-
+
def append_taxes_from_master(self, tax_parentfield, tax_master_field, tax_master_doctype=None):
if self.get(tax_master_field):
if not tax_master_doctype:
tax_master_doctype = self.meta.get_field(tax_master_field).options
-
+
tax_doctype = self.meta.get_field(tax_parentfield).options
-
+
from frappe.model import default_fields
tax_master = frappe.get_doc(tax_master_doctype, self.get(tax_master_field))
-
+
for i, tax in enumerate(tax_master.get(tax_parentfield)):
for fieldname in default_fields:
tax.set(fieldname, None)
-
+
self.append(tax_parentfield, tax)
def get_other_charges(self):
self.set("other_charges", [])
self.set_taxes("other_charges", "taxes_and_charges")
-
+
def calculate_taxes_and_totals(self):
self.discount_amount_applied = False
self._calculate_taxes_and_totals()
-
+
if self.meta.get_field("discount_amount"):
self.apply_discount_amount()
@@ -151,23 +151,23 @@
self.conversion_rate = flt(self.conversion_rate)
self.item_doclist = self.get(self.fname)
self.tax_doclist = self.get(self.other_fname)
-
+
self.calculate_item_values()
self.initialize_taxes()
-
+
if hasattr(self, "determine_exclusive_rate"):
self.determine_exclusive_rate()
-
+
self.calculate_net_total()
self.calculate_taxes()
self.calculate_totals()
self._cleanup()
-
+
def initialize_taxes(self):
for tax in self.tax_doclist:
tax.item_wise_tax_detail = {}
- tax_fields = ["total", "tax_amount_after_discount_amount",
- "tax_amount_for_current_item", "grand_total_for_current_item",
+ tax_fields = ["total", "tax_amount_after_discount_amount",
+ "tax_amount_for_current_item", "grand_total_for_current_item",
"tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
if not self.discount_amount_applied:
@@ -179,7 +179,7 @@
self.validate_on_previous_row(tax)
self.validate_inclusive_tax(tax)
self.round_floats_in(tax)
-
+
def validate_on_previous_row(self, tax):
"""
validate if a valid row id is mentioned in case of
@@ -194,7 +194,7 @@
"row_id_label": self.meta.get_label("row_id",
parentfield=self.other_fname)
})
-
+
def validate_inclusive_tax(self, tax):
def _on_previous_row_error(row_range):
throw((_("Row") + " # %(idx)s [%(doctype)s]: " +
@@ -202,24 +202,21 @@
" [" + _("Row") + " # %(row_range)s] " + _("also be included in Item's rate")) % {
"idx": tax.idx,
"doctype": tax.doctype,
- "inclusive_label": self.meta.get_label("included_in_print_rate",
- parentfield=self.other_fname),
- "charge_type_label": self.meta.get_label("charge_type",
- parentfield=self.other_fname),
+ "inclusive_label": frappe.get_meta(tax.doctype).get_label("included_in_print_rate"),
+ "charge_type_label": frappe.get_meta(tax.doctype).get_label("charge_type"),
"charge_type": tax.charge_type,
"row_range": row_range
})
-
+
if cint(getattr(tax, "included_in_print_rate", None)):
if tax.charge_type == "Actual":
# inclusive tax cannot be of type Actual
- throw((_("Row")
- + " # %(idx)s [%(doctype)s]: %(charge_type_label)s = \"%(charge_type)s\" "
+ throw((_("Row")
+ + " # %(idx)s [%(doctype)s]: %(charge_type_label)s = \"%(charge_type)s\" "
+ "cannot be included in Item's rate") % {
"idx": tax.idx,
"doctype": tax.doctype,
- "charge_type_label": self.meta.get_label("charge_type",
- parentfield=self.other_fname),
+ "charge_type_label": frappe.get_meta(tax.doctype).get_label("charge_type"),
"charge_type": tax.charge_type,
})
elif tax.charge_type == "On Previous Row Amount" and \
@@ -230,10 +227,10 @@
not all([cint(t.included_in_print_rate) for t in self.tax_doclist[:cint(tax.row_id) - 1]]):
# all rows about the reffered tax should be inclusive
_on_previous_row_error("1 - %d" % (tax.row_id,))
-
+
def calculate_taxes(self):
# maintain actual tax rate based on idx
- actual_tax_dict = dict([[tax.idx, tax.rate] for tax in self.tax_doclist
+ actual_tax_dict = dict([[tax.idx, tax.rate] for tax in self.tax_doclist
if tax.charge_type == "Actual"])
for n, item in enumerate(self.item_doclist):
@@ -258,26 +255,26 @@
tax.tax_amount += current_tax_amount
tax.tax_amount_after_discount_amount += current_tax_amount
-
+
if getattr(tax, "category", None):
# if just for valuation, do not add the tax amount in total
# hence, setting it as 0 for further steps
current_tax_amount = 0.0 if (tax.category == "Valuation") \
else current_tax_amount
-
+
current_tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
-
+
# Calculate tax.total viz. grand total till that step
- # note: grand_total_for_current_item contains the contribution of
+ # note: grand_total_for_current_item contains the contribution of
# item's amount, previously applied tax and the current tax on that item
if i==0:
tax.grand_total_for_current_item = flt(item.base_amount + current_tax_amount,
self.precision("total", tax))
else:
tax.grand_total_for_current_item = \
- flt(self.tax_doclist[i-1].grand_total_for_current_item +
+ flt(self.tax_doclist[i-1].grand_total_for_current_item +
current_tax_amount, self.precision("total", tax))
-
+
# in tax.total, accumulate grand total of each item
tax.total += tax.grand_total_for_current_item
@@ -292,12 +289,12 @@
def round_off_totals(self, tax):
tax.total = flt(tax.total, self.precision("total", tax))
tax.tax_amount = flt(tax.tax_amount, self.precision("tax_amount", tax))
- tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
+ tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
self.precision("tax_amount", tax))
def adjust_discount_amount_loss(self, tax):
discount_amount_loss = self.grand_total - flt(self.discount_amount) - tax.total
- tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount +
+ tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount +
discount_amount_loss, self.precision("tax_amount", tax))
tax.total = flt(tax.total + discount_amount_loss, self.precision("total", tax))
@@ -331,40 +328,40 @@
tax.item_wise_tax_detail[key] = [tax_rate, current_tax_amount]
return current_tax_amount
-
+
def _load_item_tax_rate(self, item_tax_rate):
return json.loads(item_tax_rate) if item_tax_rate else {}
-
+
def _get_tax_rate(self, tax, item_tax_map):
if item_tax_map.has_key(tax.account_head):
return flt(item_tax_map.get(tax.account_head), self.precision("rate", tax))
else:
return tax.rate
-
+
def _cleanup(self):
for tax in self.tax_doclist:
tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail)
-
+
def _set_in_company_currency(self, item, print_field, base_field):
"""set values in base currency"""
- value_in_company_currency = flt(self.conversion_rate *
+ value_in_company_currency = flt(self.conversion_rate *
flt(item.get(print_field), self.precision(print_field, item)),
self.precision(base_field, item))
item.set(base_field, value_in_company_currency)
-
+
def calculate_total_advance(self, parenttype, advance_parentfield):
if self.doctype == parenttype and self.docstatus < 2:
- sum_of_allocated_amount = sum([flt(adv.allocated_amount, self.precision("allocated_amount", adv))
+ sum_of_allocated_amount = sum([flt(adv.allocated_amount, self.precision("allocated_amount", adv))
for adv in self.get(advance_parentfield)])
self.total_advance = flt(sum_of_allocated_amount, self.precision("total_advance"))
-
+
self.calculate_outstanding_amount()
def get_gl_dict(self, args):
"""this method populates the common properties of a gl entry record"""
gl_dict = frappe._dict({
- 'company': self.company,
+ 'company': self.company,
'posting_date': self.posting_date,
'voucher_type': self.doctype,
'voucher_no': self.name,
@@ -377,24 +374,24 @@
})
gl_dict.update(args)
return gl_dict
-
+
def clear_unallocated_advances(self, childtype, parentfield):
self.set(parentfield, self.get(parentfield, {"allocated_amount": ["not in", [0, None, ""]]}))
- frappe.db.sql("""delete from `tab%s` where parentfield=%s and parent = %s
+ frappe.db.sql("""delete from `tab%s` where parentfield=%s and parent = %s
and ifnull(allocated_amount, 0) = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
-
+
def get_advances(self, account_head, child_doctype, parentfield, dr_or_cr):
- res = frappe.db.sql("""select t1.name as jv_no, t1.remark,
+ res = frappe.db.sql("""select t1.name as jv_no, t1.remark,
t2.%s as amount, t2.name as jv_detail_no
- from `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
- where t1.name = t2.parent and t2.account = %s and t2.is_advance = 'Yes'
+ from `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
+ where t1.name = t2.parent and t2.account = %s and t2.is_advance = 'Yes'
and (t2.against_voucher is null or t2.against_voucher = '')
- and (t2.against_invoice is null or t2.against_invoice = '')
- and (t2.against_jv is null or t2.against_jv = '')
- and t1.docstatus = 1 order by t1.posting_date""" %
+ and (t2.against_invoice is null or t2.against_invoice = '')
+ and (t2.against_jv is null or t2.against_jv = '')
+ and t1.docstatus = 1 order by t1.posting_date""" %
(dr_or_cr, '%s'), account_head, as_dict=1)
-
+
self.set(parentfield, [])
for d in res:
self.append(parentfield, {
@@ -405,74 +402,74 @@
"advance_amount": flt(d.amount),
"allocate_amount": 0
})
-
+
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
from erpnext.controllers.status_updater import get_tolerance_for
item_tolerance = {}
global_tolerance = None
-
+
for item in self.get("entries"):
if item.get(item_ref_dn):
- ref_amt = flt(frappe.db.get_value(ref_dt + " Item",
+ ref_amt = flt(frappe.db.get_value(ref_dt + " Item",
item.get(item_ref_dn), based_on), self.precision(based_on, item))
if not ref_amt:
- frappe.msgprint(_("As amount for item") + ": " + item.item_code + _(" in ") +
+ frappe.msgprint(_("As amount for item") + ": " + item.item_code + _(" in ") +
ref_dt + _(" is zero, system will not check for over-billed"))
else:
- already_billed = frappe.db.sql("""select sum(%s) from `tab%s`
- where %s=%s and docstatus=1 and parent != %s""" %
- (based_on, self.tname, item_ref_dn, '%s', '%s'),
+ already_billed = frappe.db.sql("""select sum(%s) from `tab%s`
+ where %s=%s and docstatus=1 and parent != %s""" %
+ (based_on, self.tname, item_ref_dn, '%s', '%s'),
(item.get(item_ref_dn), self.name))[0][0]
-
- total_billed_amt = flt(flt(already_billed) + flt(item.get(based_on)),
+
+ total_billed_amt = flt(flt(already_billed) + flt(item.get(based_on)),
self.precision(based_on, item))
-
- tolerance, item_tolerance, global_tolerance = get_tolerance_for(item.item_code,
+
+ tolerance, item_tolerance, global_tolerance = get_tolerance_for(item.item_code,
item_tolerance, global_tolerance)
-
+
max_allowed_amt = flt(ref_amt * (100 + tolerance) / 100)
-
+
if total_billed_amt - max_allowed_amt > 0.01:
reduce_by = total_billed_amt - max_allowed_amt
-
- frappe.throw(_("Row #") + cstr(item.idx) + ": " +
- _(" Max amount allowed for Item ") + cstr(item.item_code) +
- _(" against ") + ref_dt + " " +
- cstr(item.get(ref_dt.lower().replace(" ", "_"))) + _(" is ") +
- cstr(max_allowed_amt) + ". \n" +
+
+ frappe.throw(_("Row #") + cstr(item.idx) + ": " +
+ _(" Max amount allowed for Item ") + cstr(item.item_code) +
+ _(" against ") + ref_dt + " " +
+ cstr(item.get(ref_dt.lower().replace(" ", "_"))) + _(" is ") +
+ cstr(max_allowed_amt) + ". \n" +
_("""If you want to increase your overflow tolerance, please increase \
- tolerance % in Global Defaults or Item master.
- Or, you must reduce the amount by """) + cstr(reduce_by) + "\n" +
+ tolerance % in Global Defaults or Item master.
+ Or, you must reduce the amount by """) + cstr(reduce_by) + "\n" +
_("""Also, please check if the order item has already been billed \
in the Sales Order"""))
-
+
def get_company_default(self, fieldname):
from erpnext.accounts.utils import get_company_default
return get_company_default(self.company, fieldname)
-
+
def get_stock_items(self):
stock_items = []
- item_codes = list(set(item.item_code for item in
+ item_codes = list(set(item.item_code for item in
self.get(self.fname)))
if item_codes:
stock_items = [r[0] for r in frappe.db.sql("""select name
from `tabItem` where name in (%s) and is_stock_item='Yes'""" % \
(", ".join((["%s"]*len(item_codes))),), item_codes)]
-
+
return stock_items
-
+
@property
def company_abbr(self):
if not hasattr(self, "_abbr"):
self._abbr = frappe.db.get_value("Company", self.company, "abbr")
-
+
return self._abbr
def check_credit_limit(self, account):
total_outstanding = frappe.db.sql("""
- select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))
+ select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))
from `tabGL Entry` where account = %s""", account)
-
+
total_outstanding = total_outstanding[0][0] if total_outstanding else 0
if total_outstanding:
frappe.get_doc('Account', account).check_credit_limit(total_outstanding)
@@ -480,4 +477,4 @@
@frappe.whitelist()
def get_tax_rate(account_head):
- return frappe.db.get_value("Account", account_head, "tax_rate")
\ No newline at end of file
+ return frappe.db.get_value("Account", account_head, "tax_rate")
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 027bfd2..3f78fe6 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -13,52 +13,52 @@
def onload_post_render(self):
# contact, address, item details and pos details (if applicable)
self.set_missing_values()
-
+
def validate(self):
super(SellingController, self).validate()
self.validate_max_discount()
check_active_sales_items(self)
-
+
def get_sender(self, comm):
return frappe.db.get_value('Sales Email Settings', None, 'email_id')
-
+
def set_missing_values(self, for_validate=False):
super(SellingController, self).set_missing_values(for_validate)
-
+
# set contact and address details for customer, if they are not mentioned
self.set_missing_lead_customer_details()
self.set_price_list_and_item_details()
if self.get("__islocal"):
self.set_taxes("other_charges", "taxes_and_charges")
-
+
def set_missing_lead_customer_details(self):
if getattr(self, "customer", None):
from erpnext.accounts.party import _get_party_details
self.update_if_missing(_get_party_details(self.customer,
ignore_permissions=getattr(self, "ignore_permissions", None)))
-
+
elif getattr(self, "lead", None):
from erpnext.selling.doctype.lead.lead import get_lead_details
self.update_if_missing(get_lead_details(self.lead))
-
+
def set_price_list_and_item_details(self):
self.set_price_list_currency("Selling")
self.set_missing_item_details()
-
+
def apply_shipping_rule(self):
if self.shipping_rule:
shipping_rule = frappe.get_doc("Shipping Rule", self.shipping_rule)
value = self.net_total
-
+
# TODO
# shipping rule calculation based on item's net weight
-
+
shipping_amount = 0.0
for condition in shipping_rule.get("shipping_rule_conditions"):
if not condition.to_value or (flt(condition.from_value) <= value <= flt(condition.to_value)):
shipping_amount = condition.shipping_amount
break
-
+
self.append("other_charges", {
"doctype": "Sales Taxes and Charges",
"charge_type": "Actual",
@@ -67,86 +67,86 @@
"description": shipping_rule.label,
"rate": shipping_amount
})
-
+
def set_total_in_words(self):
from frappe.utils import money_in_words
company_currency = get_company_currency(self.company)
-
- disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None,
+
+ disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None,
"disable_rounded_total"))
-
+
if self.meta.get_field("in_words"):
- self.in_words = money_in_words(disable_rounded_total and
+ self.in_words = money_in_words(disable_rounded_total and
self.grand_total or self.rounded_total, company_currency)
if self.meta.get_field("in_words_export"):
- self.in_words_export = money_in_words(disable_rounded_total and
+ self.in_words_export = money_in_words(disable_rounded_total and
self.grand_total_export or self.rounded_total_export, self.currency)
-
+
def calculate_taxes_and_totals(self):
self.other_fname = "other_charges"
-
+
super(SellingController, self).calculate_taxes_and_totals()
-
+
self.calculate_total_advance("Sales Invoice", "advance_adjustment_details")
self.calculate_commission()
self.calculate_contribution()
-
+
def determine_exclusive_rate(self):
if not any((cint(tax.included_in_print_rate) for tax in self.tax_doclist)):
# no inclusive tax
return
-
+
for item in self.item_doclist:
item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
cumulated_tax_fraction = 0
for i, tax in enumerate(self.tax_doclist):
tax.tax_fraction_for_current_item = self.get_current_tax_fraction(tax, item_tax_map)
-
+
if i==0:
tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
else:
tax.grand_total_fraction_for_current_item = \
self.tax_doclist[i-1].grand_total_fraction_for_current_item \
+ tax.tax_fraction_for_current_item
-
+
cumulated_tax_fraction += tax.tax_fraction_for_current_item
-
+
if cumulated_tax_fraction and not self.discount_amount_applied:
item.base_amount = flt((item.amount * self.conversion_rate) /
(1 + cumulated_tax_fraction), self.precision("base_amount", item))
-
+
item.base_rate = flt(item.base_amount / item.qty, self.precision("base_rate", item))
-
+
if item.discount_percentage == 100:
item.base_price_list_rate = item.base_rate
item.base_rate = 0.0
else:
item.base_price_list_rate = flt(item.base_rate / (1 - (item.discount_percentage / 100.0)),
self.precision("base_price_list_rate", item))
-
+
def get_current_tax_fraction(self, tax, item_tax_map):
"""
Get tax fraction for calculating tax exclusive amount
from tax inclusive amount
"""
current_tax_fraction = 0
-
+
if cint(tax.included_in_print_rate):
tax_rate = self._get_tax_rate(tax, item_tax_map)
-
+
if tax.charge_type == "On Net Total":
current_tax_fraction = tax_rate / 100.0
-
+
elif tax.charge_type == "On Previous Row Amount":
current_tax_fraction = (tax_rate / 100.0) * \
self.tax_doclist[cint(tax.row_id) - 1].tax_fraction_for_current_item
-
+
elif tax.charge_type == "On Previous Row Total":
current_tax_fraction = (tax_rate / 100.0) * \
self.tax_doclist[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
-
+
return current_tax_fraction
-
+
def calculate_item_values(self):
if not self.discount_amount_applied:
for item in self.item_doclist:
@@ -171,22 +171,22 @@
for item in self.item_doclist:
self.net_total += item.base_amount
self.net_total_export += item.amount
-
+
self.round_floats_in(self, ["net_total", "net_total_export"])
-
+
def calculate_totals(self):
self.grand_total = flt(self.tax_doclist and \
self.tax_doclist[-1].total or self.net_total, self.precision("grand_total"))
- self.grand_total_export = flt(self.grand_total / self.conversion_rate,
+ self.grand_total_export = flt(self.grand_total / self.conversion_rate,
self.precision("grand_total_export"))
-
+
self.other_charges_total = flt(self.grand_total - self.net_total,
self.precision("other_charges_total"))
- self.other_charges_total_export = flt(self.grand_total_export -
- self.net_total_export + flt(self.discount_amount),
+ self.other_charges_total_export = flt(self.grand_total_export -
+ self.net_total_export + flt(self.discount_amount),
self.precision("other_charges_total_export"))
-
+
self.rounded_total = _round(self.grand_total)
self.rounded_total_export = _round(self.grand_total_export)
@@ -214,12 +214,12 @@
flt(tax.rate) / 100
actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
- grand_total_for_discount_amount = flt(self.grand_total - sum(actual_taxes_dict.values()),
+ grand_total_for_discount_amount = flt(self.grand_total - sum(actual_taxes_dict.values()),
self.precision("grand_total"))
return grand_total_for_discount_amount
def calculate_outstanding_amount(self):
- # NOTE:
+ # NOTE:
# write_off_amount is only for POS Invoice
# total_advance is only for non POS Invoice
if self.doctype == "Sales Invoice" and self.docstatus == 0:
@@ -228,14 +228,14 @@
total_amount_to_pay = self.grand_total - self.write_off_amount
self.outstanding_amount = flt(total_amount_to_pay - self.total_advance \
- self.paid_amount, self.precision("outstanding_amount"))
-
+
def calculate_commission(self):
if self.meta.get_field("commission_rate"):
self.round_floats_in(self, ["net_total", "commission_rate"])
if self.commission_rate > 100.0:
- msgprint(_(self.meta.get_label("commission_rate")) + " " +
+ msgprint(_(self.meta.get_label("commission_rate")) + " " +
_("cannot be greater than 100"), raise_exception=True)
-
+
self.total_commission = flt(self.net_total * self.commission_rate / 100.0,
self.precision("total_commission"))
@@ -248,66 +248,66 @@
sales_person.allocated_amount = flt(
self.net_total * sales_person.allocated_percentage / 100.0,
self.precision("allocated_amount", sales_person))
-
+
total += sales_person.allocated_percentage
-
+
if sales_team and total != 100.0:
- msgprint(_("Total") + " " +
- _(self.meta.get_label("allocated_percentage", parentfield="sales_team")) +
+ msgprint(_("Total") + " " +
+ _(self.meta.get_label("allocated_percentage", parentfield="sales_team")) +
" " + _("should be 100%"), raise_exception=True)
-
+
def validate_order_type(self):
valid_types = ["Sales", "Maintenance", "Shopping Cart"]
if not self.order_type:
self.order_type = "Sales"
elif self.order_type not in valid_types:
- msgprint(_(self.meta.get_label("order_type")) + " " +
+ msgprint(_(self.meta.get_label("order_type")) + " " +
_("must be one of") + ": " + comma_or(valid_types), raise_exception=True)
-
+
def check_credit(self, grand_total):
- customer_account = frappe.db.get_value("Account", {"company": self.company,
+ customer_account = frappe.db.get_value("Account", {"company": self.company,
"master_name": self.customer}, "name")
if customer_account:
- total_outstanding = frappe.db.sql("""select
- sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))
+ total_outstanding = frappe.db.sql("""select
+ sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))
from `tabGL Entry` where account = %s""", customer_account)
total_outstanding = total_outstanding[0][0] if total_outstanding else 0
-
+
outstanding_including_current = flt(total_outstanding) + flt(grand_total)
- frappe.get_doc('Account', customer_account).run_method("check_credit_limit",
+ frappe.get_doc('Account', customer_account).run_method("check_credit_limit",
outstanding_including_current)
-
+
def validate_max_discount(self):
for d in self.get(self.fname):
discount = flt(frappe.db.get_value("Item", d.item_code, "max_discount"))
-
+
if discount and flt(d.discount_percentage) > discount:
- frappe.throw(_("You cannot give more than ") + cstr(discount) + "% " +
+ frappe.throw(_("You cannot give more than ") + cstr(discount) + "% " +
_("discount on Item Code") + ": " + cstr(d.item_code))
-
+
def get_item_list(self):
il = []
for d in self.get(self.fname):
reserved_warehouse = ""
reserved_qty_for_main_item = 0
-
+
if self.doctype == "Sales Order":
- if (frappe.db.get_value("Item", d.item_code, "is_stock_item") == 'Yes' or
+ if (frappe.db.get_value("Item", d.item_code, "is_stock_item") == 'Yes' or
self.has_sales_bom(d.item_code)) and not d.warehouse:
- frappe.throw(_("Please enter Reserved Warehouse for item ") +
+ frappe.throw(_("Please enter Reserved Warehouse for item ") +
d.item_code + _(" as it is stock Item or packing item"))
reserved_warehouse = d.warehouse
if flt(d.qty) > flt(d.delivered_qty):
reserved_qty_for_main_item = flt(d.qty) - flt(d.delivered_qty)
-
- if self.doctype == "Delivery Note" and d.against_sales_order:
+
+ elif self.doctype == "Delivery Note" and d.against_sales_order:
# if SO qty is 10 and there is tolerance of 20%, then it will allow DN of 12.
# But in this case reserved qty should only be reduced by 10 and not 12
-
- already_delivered_qty = self.get_already_delivered_qty(self.name,
+
+ already_delivered_qty = self.get_already_delivered_qty(self.name,
d.against_sales_order, d.prevdoc_detail_docname)
so_qty, reserved_warehouse = self.get_so_qty_and_warehouse(d.prevdoc_detail_docname)
-
+
if already_delivered_qty + d.qty > so_qty:
reserved_qty_for_main_item = -(so_qty - already_delivered_qty)
else:
@@ -341,15 +341,15 @@
'name': d.name
}))
return il
-
+
def has_sales_bom(self, item_code):
- return frappe.db.sql("""select name from `tabSales BOM`
+ return frappe.db.sql("""select name from `tabSales BOM`
where new_item_code=%s and docstatus != 2""", item_code)
-
+
def get_already_delivered_qty(self, dn, so, so_detail):
- qty = frappe.db.sql("""select sum(qty) from `tabDelivery Note Item`
- where prevdoc_detail_docname = %s and docstatus = 1
- and against_sales_order = %s
+ qty = frappe.db.sql("""select sum(qty) from `tabDelivery Note Item`
+ where prevdoc_detail_docname = %s and docstatus = 1
+ and against_sales_order = %s
and parent != %s""", (so_detail, so, dn))
return qty and flt(qty[0][0]) or 0.0
@@ -359,24 +359,24 @@
so_qty = so_item and flt(so_item[0]["qty"]) or 0.0
so_warehouse = so_item and so_item[0]["warehouse"] or ""
return so_qty, so_warehouse
-
+
def check_stop_sales_order(self, ref_fieldname):
for d in self.get(self.fname):
if d.get(ref_fieldname):
status = frappe.db.get_value("Sales Order", d.get(ref_fieldname), "status")
if status == "Stopped":
- frappe.throw(self.doctype +
- _(" can not be created/modified against stopped Sales Order ") +
+ frappe.throw(self.doctype +
+ _(" can not be created/modified against stopped Sales Order ") +
d.get(ref_fieldname))
-
+
def check_active_sales_items(obj):
for d in obj.get(obj.fname):
if d.item_code:
- item = frappe.db.sql("""select docstatus, is_sales_item,
- is_service_item, income_account from tabItem where name = %s""",
+ item = frappe.db.sql("""select docstatus, is_sales_item,
+ is_service_item, income_account from tabItem where name = %s""",
d.item_code, as_dict=True)[0]
if item.is_sales_item == 'No' and item.is_service_item == 'No':
frappe.throw(_("Item is neither Sales nor Service Item") + ": " + d.item_code)
if getattr(d, "income_account", None) and not item.income_account:
- frappe.db.set_value("Item", d.item_code, "income_account",
+ frappe.db.set_value("Item", d.item_code, "income_account",
d.income_account)
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index aa96b7c..206c963 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -14,15 +14,15 @@
class BOM(Document):
def autoname(self):
- last_name = frappe.db.sql("""select max(name) from `tabBOM`
+ last_name = frappe.db.sql("""select max(name) from `tabBOM`
where name like "BOM/%s/%%" """ % cstr(self.item).replace('"', '\\"'))
if last_name:
idx = cint(cstr(last_name[0][0]).split('/')[-1].split('-')[0]) + 1
-
+
else:
idx = 1
self.name = 'BOM/' + self.item + ('/%.3i' % idx)
-
+
def validate(self):
self.clear_operations()
self.validate_main_item()
@@ -34,12 +34,11 @@
self.validate_materials()
self.set_bom_material_details()
self.calculate_cost()
-
+
def on_update(self):
self.check_recursion()
self.update_exploded_items()
- self.db_update()
-
+
def on_submit(self):
self.manage_default_bom()
@@ -50,48 +49,48 @@
# check if used in any other bom
self.validate_bom_links()
self.manage_default_bom()
-
+
def on_update_after_submit(self):
self.validate_bom_links()
self.manage_default_bom()
def get_item_det(self, item_code):
- item = frappe.db.sql("""select name, is_asset_item, is_purchase_item,
- docstatus, description, is_sub_contracted_item, stock_uom, default_bom,
- last_purchase_rate, standard_rate, is_manufactured_item
+ item = frappe.db.sql("""select name, is_asset_item, is_purchase_item,
+ docstatus, description, is_sub_contracted_item, stock_uom, default_bom,
+ last_purchase_rate, standard_rate, is_manufactured_item
from `tabItem` where name=%s""", item_code, as_dict = 1)
return item
-
+
def validate_rm_item(self, item):
if item[0]['name'] == self.item:
- msgprint("Item_code: %s in materials tab cannot be same as FG Item",
+ msgprint("Item_code: %s in materials tab cannot be same as FG Item",
item[0]['name'], raise_exception=1)
-
+
if not item or item[0]['docstatus'] == 2:
msgprint("Item %s does not exist in system" % item[0]['item_code'], raise_exception = 1)
-
+
def set_bom_material_details(self):
for item in self.get("bom_materials"):
- ret = self.get_bom_material_detail({"item_code": item.item_code, "bom_no": item.bom_no,
+ ret = self.get_bom_material_detail({"item_code": item.item_code, "bom_no": item.bom_no,
"qty": item.qty})
for r in ret:
if not item.get(r):
item.set(r, ret[r])
-
+
def get_bom_material_detail(self, args=None):
""" Get raw material details like uom, desc and rate"""
if not args:
args = frappe.form_dict.get('args')
-
+
if isinstance(args, basestring):
import json
args = json.loads(args)
-
+
item = self.get_item_det(args['item_code'])
self.validate_rm_item(item)
-
+
args['bom_no'] = args['bom_no'] or item and cstr(item[0]['default_bom']) or ''
args.update(item[0])
@@ -117,27 +116,23 @@
elif self.rm_cost_as_per == "Price List":
if not self.buying_price_list:
frappe.throw(_("Please select Price List"))
- rate = frappe.db.get_value("Item Price", {"price_list": self.buying_price_list,
+ rate = frappe.db.get_value("Item Price", {"price_list": self.buying_price_list,
"item_code": arg["item_code"]}, "price_list_rate") or 0
elif self.rm_cost_as_per == 'Standard Rate':
rate = arg['standard_rate']
return rate
-
+
def update_cost(self):
for d in self.get("bom_materials"):
d.rate = self.get_bom_material_detail({
- 'item_code': d.item_code,
+ 'item_code': d.item_code,
'bom_no': d.bom_no,
'qty': d.qty
})["rate"]
-
- if self.docstatus == 0:
+
+ if self.docstatus in (0, 1):
self.save()
- elif self.docstatus == 1:
- self.calculate_cost()
- self.update_exploded_items()
- self.update_after_submit()
def get_bom_unitcost(self, bom_no):
bom = frappe.db.sql("""select name, total_cost/quantity as unit_cost from `tabBOM`
@@ -145,9 +140,9 @@
return bom and bom[0]['unit_cost'] or 0
def get_valuation_rate(self, args):
- """ Get average valuation rate of relevant warehouses
- as per valuation method (MAR/FIFO)
- as on costing date
+ """ Get average valuation rate of relevant warehouses
+ as per valuation method (MAR/FIFO)
+ as on costing date
"""
from erpnext.stock.utils import get_incoming_rate
dt = self.costing_date or nowdate()
@@ -168,19 +163,19 @@
return rate and flt(sum(rate))/len(rate) or 0
def manage_default_bom(self):
- """ Uncheck others if current one is selected as default,
+ """ Uncheck others if current one is selected as default,
update default bom in item master
"""
if self.is_default and self.is_active:
from frappe.model.utils import set_default
set_default(self, "item")
frappe.db.set_value("Item", self.item, "default_bom", self.name)
-
+
else:
if not self.is_active:
frappe.db.set(self, "is_default", 0)
-
- frappe.db.sql("update `tabItem` set default_bom = null where name = %s and default_bom = %s",
+
+ frappe.db.sql("update `tabItem` set default_bom = null where name = %s and default_bom = %s",
(self.item, self.name))
def clear_operations(self):
@@ -193,7 +188,7 @@
""" Validate main FG item"""
item = self.get_item_det(self.item)
if not item:
- msgprint("Item %s does not exists in the system or expired." %
+ msgprint("Item %s does not exists in the system or expired." %
self.item, raise_exception = 1)
elif item[0]['is_manufactured_item'] != 'Yes' \
and item[0]['is_sub_contracted_item'] != 'Yes':
@@ -209,7 +204,7 @@
self.op = []
for d in self.get('bom_operations'):
if cstr(d.operation_no) in self.op:
- msgprint("Operation no: %s is repeated in Operations Table" %
+ msgprint("Operation no: %s is repeated in Operations Table" %
d.operation_no, raise_exception=1)
else:
# add operation in op list
@@ -222,36 +217,36 @@
# check if operation no not in op table
if self.with_operations and cstr(m.operation_no) not in self.op:
msgprint("""Operation no: %s against item: %s at row no: %s \
- is not present at Operations table""" %
+ is not present at Operations table""" %
(m.operation_no, m.item_code, m.idx), raise_exception = 1)
-
+
item = self.get_item_det(m.item_code)
if item[0]['is_manufactured_item'] == 'Yes':
if not m.bom_no:
- msgprint("Please enter BOM No aginst item: %s at row no: %s" %
+ msgprint("Please enter BOM No aginst item: %s at row no: %s" %
(m.item_code, m.idx), raise_exception=1)
else:
self.validate_bom_no(m.item_code, m.bom_no, m.idx)
elif m.bom_no:
msgprint("""As Item %s is not a manufactured / sub-contracted item, \
- you can not enter BOM against it (Row No: %s).""" %
+ you can not enter BOM against it (Row No: %s).""" %
(m.item_code, m.idx), raise_exception = 1)
if flt(m.qty) <= 0:
- msgprint("Please enter qty against raw material: %s at row no: %s" %
+ msgprint("Please enter qty against raw material: %s at row no: %s" %
(m.item_code, m.idx), raise_exception = 1)
self.check_if_item_repeated(m.item_code, m.operation_no, check_list)
def validate_bom_no(self, item, bom_no, idx):
"""Validate BOM No of sub-contracted items"""
- bom = frappe.db.sql("""select name from `tabBOM` where name = %s and item = %s
- and is_active=1 and docstatus=1""",
+ bom = frappe.db.sql("""select name from `tabBOM` where name = %s and item = %s
+ and is_active=1 and docstatus=1""",
(bom_no, item), as_dict =1)
if not bom:
msgprint("""Incorrect BOM No: %s against item: %s at row no: %s.
- It may be inactive or not submitted or does not belong to this item.""" %
+ It may be inactive or not submitted or does not belong to this item.""" %
(bom_no, item, idx), raise_exception = 1)
def check_if_item_repeated(self, item, op, check_list):
@@ -268,7 +263,7 @@
for d in check_list:
bom_list, count = [self.name], 0
while (len(bom_list) > count ):
- boms = frappe.db.sql(" select %s from `tabBOM Item` where %s = %s " %
+ boms = frappe.db.sql(" select %s from `tabBOM Item` where %s = %s " %
(d[0], d[1], '%s'), cstr(bom_list[count]))
count = count + 1
for b in boms:
@@ -277,24 +272,24 @@
""" % (cstr(b[0]), cstr(d[2]), self.name), raise_exception = 1)
if b[0]:
bom_list.append(b[0])
-
+
def update_cost_and_exploded_items(self, bom_list=[]):
bom_list = self.traverse_tree(bom_list)
for bom in bom_list:
bom_obj = frappe.get_doc("BOM", bom)
bom_obj.on_update()
-
+
return bom_list
-
+
def traverse_tree(self, bom_list=[]):
def _get_children(bom_no):
- return [cstr(d[0]) for d in frappe.db.sql("""select bom_no from `tabBOM Item`
+ return [cstr(d[0]) for d in frappe.db.sql("""select bom_no from `tabBOM Item`
where parent = %s and ifnull(bom_no, '') != ''""", bom_no)]
-
+
count = 0
if self.name not in bom_list:
bom_list.append(self.name)
-
+
while(count < len(bom_list)):
for child_bom in _get_children(bom_list[count]):
if child_bom not in bom_list:
@@ -302,7 +297,7 @@
count += 1
bom_list.reverse()
return bom_list
-
+
def calculate_cost(self):
"""Calculate bom totals"""
self.calculate_op_cost()
@@ -319,7 +314,7 @@
d.operating_cost = flt(d.hour_rate) * flt(d.time_in_mins) / 60.0
total_op_cost += flt(d.operating_cost)
self.operating_cost = total_op_cost
-
+
def calculate_rm_cost(self):
"""Fetch RM rate as per today's valuation rate and calculate totals"""
total_rm_cost = 0
@@ -329,7 +324,7 @@
d.amount = flt(d.rate) * flt(d.qty)
d.qty_consumed_per_unit = flt(d.qty) / flt(self.quantity)
total_rm_cost += d.amount
-
+
self.raw_material_cost = total_rm_cost
def update_exploded_items(self):
@@ -345,38 +340,38 @@
self.get_child_exploded_items(d.bom_no, d.qty)
else:
self.add_to_cur_exploded_items(frappe._dict({
- 'item_code' : d.item_code,
- 'description' : d.description,
- 'stock_uom' : d.stock_uom,
+ 'item_code' : d.item_code,
+ 'description' : d.description,
+ 'stock_uom' : d.stock_uom,
'qty' : flt(d.qty),
'rate' : flt(d.rate),
}))
-
+
def add_to_cur_exploded_items(self, args):
if self.cur_exploded_items.get(args.item_code):
self.cur_exploded_items[args.item_code]["qty"] += args.qty
else:
self.cur_exploded_items[args.item_code] = args
-
+
def get_child_exploded_items(self, bom_no, qty):
""" Add all items from Flat BOM of child BOM"""
-
- child_fb_items = frappe.db.sql("""select item_code, description, stock_uom, qty, rate,
- qty_consumed_per_unit from `tabBOM Explosion Item`
+
+ child_fb_items = frappe.db.sql("""select item_code, description, stock_uom, qty, rate,
+ qty_consumed_per_unit from `tabBOM Explosion Item`
where parent = %s and docstatus = 1""", bom_no, as_dict = 1)
-
+
for d in child_fb_items:
self.add_to_cur_exploded_items(frappe._dict({
- 'item_code' : d['item_code'],
- 'description' : d['description'],
- 'stock_uom' : d['stock_uom'],
+ 'item_code' : d['item_code'],
+ 'description' : d['description'],
+ 'stock_uom' : d['stock_uom'],
'qty' : flt(d['qty_consumed_per_unit'])*qty,
'rate' : flt(d['rate']),
}))
def add_exploded_items(self):
"Add items to Flat BOM table"
- self.set('flat_bom_details', [])
+ frappe.db.sql("""delete from `tabBOM Explosion Item` where parent=%s""", self.name)
for d in self.cur_exploded_items:
ch = self.append('flat_bom_details', {})
for i in self.cur_exploded_items[d].keys():
@@ -384,7 +379,7 @@
ch.amount = flt(ch.qty) * flt(ch.rate)
ch.qty_consumed_per_unit = flt(ch.qty) / flt(self.quantity)
ch.docstatus = self.docstatus
- ch.db_update()
+ ch.db_insert()
def validate_bom_links(self):
if not self.is_active:
@@ -399,26 +394,27 @@
raise_exception=1)
def get_bom_items_as_dict(bom, qty=1, fetch_exploded=1):
+ import json
item_dict = {}
-
- query = """select
+
+ query = """select
bom_item.item_code,
item.item_name,
- ifnull(sum(bom_item.qty_consumed_per_unit),0) * %(qty)s as qty,
- item.description,
+ ifnull(sum(bom_item.qty_consumed_per_unit),0) * %(qty)s as qty,
+ item.description,
item.stock_uom,
item.default_warehouse,
item.expense_account as expense_account,
item.buying_cost_center as cost_center
- from
- `tab%(table)s` bom_item, `tabItem` item
- where
- bom_item.docstatus < 2
+ from
+ `tab%(table)s` bom_item, `tabItem` item
+ where
+ bom_item.docstatus < 2
and bom_item.parent = "%(bom)s"
- and item.name = bom_item.item_code
+ and item.name = bom_item.item_code
%(conditions)s
group by item_code, stock_uom"""
-
+
if fetch_exploded:
items = frappe.db.sql(query % {
"qty": qty,
@@ -441,7 +437,7 @@
item_dict[item.item_code]["qty"] += flt(item.qty)
else:
item_dict[item.item_code] = item
-
+
return item_dict
@frappe.whitelist()
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index 074daa4..28ee49a 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -12,19 +12,19 @@
def test_get_items(self):
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
items_dict = get_bom_items_as_dict(bom="BOM/_Test FG Item 2/001", qty=1, fetch_exploded=0)
- self.assertTrue(test_records[2][1]["item_code"] in items_dict)
- self.assertTrue(test_records[2][2]["item_code"] in items_dict)
+ self.assertTrue(test_records[2]["bom_materials"][0]["item_code"] in items_dict)
+ self.assertTrue(test_records[2]["bom_materials"][1]["item_code"] in items_dict)
self.assertEquals(len(items_dict.values()), 2)
-
+
def test_get_items_exploded(self):
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
items_dict = get_bom_items_as_dict(bom="BOM/_Test FG Item 2/001", qty=1, fetch_exploded=1)
- self.assertTrue(test_records[2][1]["item_code"] in items_dict)
- self.assertFalse(test_records[2][2]["item_code"] in items_dict)
- self.assertTrue(test_records[0][1]["item_code"] in items_dict)
- self.assertTrue(test_records[0][2]["item_code"] in items_dict)
+ self.assertTrue(test_records[2]["bom_materials"][0]["item_code"] in items_dict)
+ self.assertFalse(test_records[2]["bom_materials"][1]["item_code"] in items_dict)
+ self.assertTrue(test_records[0]["bom_materials"][0]["item_code"] in items_dict)
+ self.assertTrue(test_records[0]["bom_materials"][1]["item_code"] in items_dict)
self.assertEquals(len(items_dict.values()), 3)
-
+
def test_get_items_list(self):
from erpnext.manufacturing.doctype.bom.bom import get_bom_items
- self.assertEquals(len(get_bom_items(bom="BOM/_Test FG Item 2/001", qty=1, fetch_exploded=1)), 3)
\ No newline at end of file
+ self.assertEquals(len(get_bom_items(bom="BOM/_Test FG Item 2/001", qty=1, fetch_exploded=1)), 3)
diff --git a/erpnext/manufacturing/doctype/production_order/test_production_order.py b/erpnext/manufacturing/doctype/production_order/test_production_order.py
index 81fc616..9bc001d 100644
--- a/erpnext/manufacturing/doctype/production_order/test_production_order.py
+++ b/erpnext/manufacturing/doctype/production_order/test_production_order.py
@@ -16,51 +16,53 @@
frappe.db.sql("delete from `tabStock Ledger Entry`")
frappe.db.sql("""delete from `tabBin`""")
frappe.db.sql("""delete from `tabGL Entry`""")
-
+
pro_doc = frappe.copy_doc(test_records[0])
pro_doc.insert()
pro_doc.submit()
-
+
from erpnext.stock.doctype.stock_entry.test_stock_entry import test_records as se_test_records
mr1 = frappe.copy_doc(se_test_records[0])
mr1.insert()
mr1.submit()
-
+
mr2 = frappe.copy_doc(se_test_records[0])
mr2.get("mtn_details")[0].item_code = "_Test Item Home Desktop 100"
mr2.insert()
mr2.submit()
-
+
stock_entry = make_stock_entry(pro_doc.name, "Manufacture/Repack")
stock_entry = frappe.get_doc(stock_entry)
stock_entry.fiscal_year = "_Test Fiscal Year 2013"
stock_entry.fg_completed_qty = 4
stock_entry.posting_date = "2013-05-12"
stock_entry.fiscal_year = "_Test Fiscal Year 2013"
+ stock_entry.set("mtn_details", [])
stock_entry.run_method("get_items")
stock_entry.submit()
-
- self.assertEqual(frappe.db.get_value("Production Order", pro_doc.name,
+
+ self.assertEqual(frappe.db.get_value("Production Order", pro_doc.name,
"produced_qty"), 4)
- self.assertEqual(frappe.db.get_value("Bin", {"item_code": "_Test FG Item",
+ self.assertEqual(frappe.db.get_value("Bin", {"item_code": "_Test FG Item",
"warehouse": "_Test Warehouse 1 - _TC"}, "planned_qty"), 6)
-
+
return pro_doc.name
-
+
def test_over_production(self):
from erpnext.stock.doctype.stock_entry.stock_entry import StockOverProductionError
pro_order = self.test_planned_qty()
-
+
stock_entry = make_stock_entry(pro_order, "Manufacture/Repack")
stock_entry = frappe.get_doc(stock_entry)
stock_entry.posting_date = "2013-05-12"
stock_entry.fiscal_year = "_Test Fiscal Year 2013"
stock_entry.fg_completed_qty = 15
+ stock_entry.set("mtn_details", [])
stock_entry.run_method("get_items")
stock_entry.insert()
-
- self.assertRaises(StockOverProductionError, stock_entry.submit)
-
-
-test_records = frappe.get_test_records('Production Order')
\ No newline at end of file
+ self.assertRaises(StockOverProductionError, stock_entry.submit)
+
+
+
+test_records = frappe.get_test_records('Production Order')
diff --git a/erpnext/projects/doctype/time_log/test_time_log.py b/erpnext/projects/doctype/time_log/test_time_log.py
index eedad0b..7aadf5c 100644
--- a/erpnext/projects/doctype/time_log/test_time_log.py
+++ b/erpnext/projects/doctype/time_log/test_time_log.py
@@ -7,8 +7,9 @@
from erpnext.projects.doctype.time_log.time_log import OverlapError
class TestTimeLog(unittest.TestCase):
- def test_duplication(self):
+ def test_duplication(self):
ts = frappe.get_doc(frappe.copy_doc(test_records[0]))
self.assertRaises(OverlapError, ts.insert)
-test_records = frappe.get_test_records('Time Log')
\ No newline at end of file
+test_records = frappe.get_test_records('Time Log')
+test_ignore = ["Time Log Batch", "Sales Invoice"]
diff --git a/erpnext/projects/doctype/time_log_batch/test_time_log_batch.py b/erpnext/projects/doctype/time_log_batch/test_time_log_batch.py
index 004c364..fdbc210 100644
--- a/erpnext/projects/doctype/time_log_batch/test_time_log_batch.py
+++ b/erpnext/projects/doctype/time_log_batch/test_time_log_batch.py
@@ -14,10 +14,10 @@
})
time_log.insert()
time_log.submit()
-
+
self.assertEquals(frappe.db.get_value("Time Log", time_log.name, "status"), "Submitted")
tlb = frappe.copy_doc(test_records[0])
- tlb["time_log_batch_details"][0].time_log = time_log.name
+ tlb.get("time_log_batch_details")[0].time_log = time_log.name
tlb.insert()
tlb.submit()
@@ -25,4 +25,6 @@
tlb.cancel()
self.assertEquals(frappe.db.get_value("Time Log", time_log.name, "status"), "Submitted")
-test_records = frappe.get_test_records('Time Log Batch')
\ No newline at end of file
+test_records = frappe.get_test_records('Time Log Batch')
+test_dependencies = ["Time Log"]
+test_ignore = ["Sales Invoice"]
diff --git a/erpnext/projects/doctype/time_log_batch/time_log_batch.py b/erpnext/projects/doctype/time_log_batch/time_log_batch.py
index 4ad8130..b8204e4 100644
--- a/erpnext/projects/doctype/time_log_batch/time_log_batch.py
+++ b/erpnext/projects/doctype/time_log_batch/time_log_batch.py
@@ -31,17 +31,17 @@
if tl.status != "Submitted" and self.docstatus == 0:
frappe.msgprint(_("Time Log must have status 'Submitted'") + \
" :" + tl.name + " (" + _(tl.status) + ")", raise_exception=True)
-
+
def set_status(self):
self.status = {
"0": "Draft",
"1": "Submitted",
"2": "Cancelled"
}[str(self.docstatus or 0)]
-
+
if self.sales_invoice:
self.status = "Billed"
-
+
def on_submit(self):
self.update_status(self.name)
@@ -57,4 +57,4 @@
tl = frappe.get_doc("Time Log", d.time_log)
tl.time_log_batch = time_log_batch
tl.sales_invoice = self.sales_invoice
- tl.update_after_submit()
\ No newline at end of file
+ tl.save()
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index e5364d0..361f4fa 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -12,7 +12,7 @@
from erpnext.accounts.party import create_party_account
class Customer(TransactionBase):
-
+
def autoname(self):
cust_master_name = frappe.defaults.get_global_default('cust_master_name')
if cust_master_name == 'Customer Name':
@@ -24,7 +24,7 @@
def get_company_abbr(self):
return frappe.db.get_value('Company', self.company, 'abbr')
-
+
def validate_values(self):
if frappe.defaults.get_global_default('cust_master_name') == 'Naming Series' and not self.naming_series:
frappe.throw("Series is Mandatory.", frappe.MandatoryError)
@@ -37,28 +37,27 @@
frappe.db.sql("update `tabLead` set status='Converted' where name = %s", self.lead_name)
def update_address(self):
- frappe.db.sql("""update `tabAddress` set customer_name=%s, modified=NOW()
+ frappe.db.sql("""update `tabAddress` set customer_name=%s, modified=NOW()
where customer=%s""", (self.customer_name, self.name))
def update_contact(self):
- frappe.db.sql("""update `tabContact` set customer_name=%s, modified=NOW()
+ frappe.db.sql("""update `tabContact` set customer_name=%s, modified=NOW()
where customer=%s""", (self.customer_name, self.name))
def update_credit_days_limit(self):
- frappe.db.sql("""update tabAccount set credit_days = %s, credit_limit = %s
- where master_type='Customer' and master_name = %s""",
+ frappe.db.sql("""update tabAccount set credit_days = %s, credit_limit = %s
+ where master_type='Customer' and master_name = %s""",
(self.credit_days or 0, self.credit_limit or 0, self.name))
def create_lead_address_contact(self):
if self.lead_name:
- if not frappe.db.get_value("Address", {"lead": self.lead_name, "customer": self.customer}):
- frappe.db.sql("""update `tabAddress` set customer=%s, customer_name=%s where lead=%s""",
+ if not frappe.db.get_value("Address", {"lead": self.lead_name, "customer": self.name}):
+ frappe.db.sql("""update `tabAddress` set customer=%s, customer_name=%s where lead=%s""",
(self.name, self.customer_name, self.lead_name))
lead = frappe.db.get_value("Lead", self.lead_name, ["lead_name", "email_id", "phone", "mobile_no"], as_dict=True)
- c = frappe.get_doc('Contact')
- c.set("__islocal", 1)
- c.first_name = lead.lead_name
+ c = frappe.new_doc('Contact')
+ c.first_name = lead.lead_name
c.email_id = lead.email_id
c.phone = lead.phone
c.mobile_no = lead.mobile_no
@@ -72,7 +71,7 @@
def on_update(self):
self.validate_name_with_customer_group()
-
+
self.update_lead_status()
self.update_address()
self.update_contact()
@@ -84,29 +83,29 @@
self.update_credit_days_limit()
#create address and contact from lead
self.create_lead_address_contact()
-
+
def validate_name_with_customer_group(self):
if frappe.db.exists("Customer Group", self.name):
frappe.msgprint("An Customer Group exists with same name (%s), \
- please change the Customer name or rename the Customer Group" %
+ please change the Customer name or rename the Customer Group" %
self.name, raise_exception=1)
def delete_customer_address(self):
addresses = frappe.db.sql("""select name, lead from `tabAddress`
where customer=%s""", (self.name,))
-
+
for name, lead in addresses:
if lead:
frappe.db.sql("""update `tabAddress` set customer=null, customer_name=null
where name=%s""", name)
else:
frappe.db.sql("""delete from `tabAddress` where name=%s""", name)
-
+
def delete_customer_contact(self):
- for contact in frappe.db.sql_list("""select name from `tabContact`
+ for contact in frappe.db.sql_list("""select name from `tabContact`
where customer=%s""", self.name):
frappe.delete_doc("Contact", contact)
-
+
def delete_customer_account(self):
"""delete customer's ledger if exist and check balance before deletion"""
acc = frappe.db.sql("select name from `tabAccount` where master_type = 'Customer' \
@@ -120,7 +119,7 @@
self.delete_customer_account()
if self.lead_name:
frappe.db.sql("update `tabLead` set status='Interested' where name=%s",self.lead_name)
-
+
def before_rename(self, olddn, newdn, merge=False):
from erpnext.accounts.utils import rename_account_for
rename_account_for("Customer", olddn, newdn, merge, self.company)
@@ -134,7 +133,7 @@
self.update_customer_address(newdn, set_field)
def update_customer_address(self, newdn, set_field):
- frappe.db.sql("""update `tabAddress` set address_title=%(newdn)s
+ frappe.db.sql("""update `tabAddress` set address_title=%(newdn)s
{set_field} where customer=%(newdn)s"""\
.format(set_field=set_field), ({"newdn": newdn}))
@@ -142,21 +141,21 @@
def get_dashboard_info(customer):
if not frappe.has_permission("Customer", "read", customer):
frappe.msgprint("No Permission", raise_exception=True)
-
+
out = {}
for doctype in ["Opportunity", "Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]:
- out[doctype] = frappe.db.get_value(doctype,
+ out[doctype] = frappe.db.get_value(doctype,
{"customer": customer, "docstatus": ["!=", 2] }, "count(*)")
-
- billing = frappe.db.sql("""select sum(grand_total), sum(outstanding_amount)
- from `tabSales Invoice`
- where customer=%s
+
+ billing = frappe.db.sql("""select sum(grand_total), sum(outstanding_amount)
+ from `tabSales Invoice`
+ where customer=%s
and docstatus = 1
and fiscal_year = %s""", (customer, frappe.db.get_default("fiscal_year")))
-
+
out["total_billing"] = billing[0][0]
out["total_unpaid"] = billing[0][1]
-
+
return out
@@ -165,11 +164,11 @@
fields = ["name", "customer_group", "territory"]
else:
fields = ["name", "customer_name", "customer_group", "territory"]
-
- return frappe.db.sql("""select %s from `tabCustomer` where docstatus < 2
- and (%s like %s or customer_name like %s) order by
+
+ return frappe.db.sql("""select %s from `tabCustomer` where docstatus < 2
+ and (%s like %s or customer_name like %s) order by
case when name like %s then 0 else 1 end,
case when customer_name like %s then 0 else 1 end,
- name, customer_name limit %s, %s""" %
- (", ".join(fields), searchfield, "%s", "%s", "%s", "%s", "%s", "%s"),
- ("%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, start, page_len))
\ No newline at end of file
+ name, customer_name limit %s, %s""" %
+ (", ".join(fields), searchfield, "%s", "%s", "%s", "%s", "%s", "%s"),
+ ("%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, start, page_len))
diff --git a/erpnext/selling/doctype/lead/lead.py b/erpnext/selling/doctype/lead/lead.py
index 57e73d0..8163be3 100644
--- a/erpnext/selling/doctype/lead/lead.py
+++ b/erpnext/selling/doctype/lead/lead.py
@@ -96,7 +96,7 @@
}
}}, target_doc, set_missing_values, ignore_permissions=ignore_permissions)
- return doclist.as_dict()
+ return doclist
@frappe.whitelist()
def make_opportunity(source_name, target_doc=None):
diff --git a/erpnext/selling/doctype/lead/test_lead.py b/erpnext/selling/doctype/lead/test_lead.py
index 504f839..606e328 100644
--- a/erpnext/selling/doctype/lead/test_lead.py
+++ b/erpnext/selling/doctype/lead/test_lead.py
@@ -13,9 +13,9 @@
from erpnext.selling.doctype.lead.lead import make_customer
customer = make_customer("_T-Lead-00001")
- self.assertEquals(customer[0]["doctype"], "Customer")
- self.assertEquals(customer[0]["lead_name"], "_T-Lead-00001")
+ self.assertEquals(customer.doctype, "Customer")
+ self.assertEquals(customer.lead_name, "_T-Lead-00001")
- customer[0]["company"] = "_Test Company"
- customer[0]["customer_group"] = "_Test Customer Group"
- frappe.get_doc(customer).insert()
+ customer.company = "_Test Company"
+ customer.customer_group = "_Test Customer Group"
+ customer.insert()
diff --git a/erpnext/selling/doctype/opportunity/opportunity.py b/erpnext/selling/doctype/opportunity/opportunity.py
index a2358a3..c87c983 100644
--- a/erpnext/selling/doctype/opportunity/opportunity.py
+++ b/erpnext/selling/doctype/opportunity/opportunity.py
@@ -161,4 +161,4 @@
}
}, target_doc, set_missing_values)
- return doclist.as_dict()
\ No newline at end of file
+ return doclist
\ No newline at end of file
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index f5c2050..d6ade9e 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -137,7 +137,7 @@
# postprocess: fetch shipping address, set missing values
- return doclist.as_dict()
+ return doclist
def _make_customer(source_name, ignore_permissions=False):
quotation = frappe.db.get_value("Quotation", source_name, ["lead", "order_type"])
diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py
index 54aa1c4..d393a3d 100644
--- a/erpnext/selling/doctype/quotation/test_quotation.py
+++ b/erpnext/selling/doctype/quotation/test_quotation.py
@@ -10,26 +10,26 @@
class TestQuotation(unittest.TestCase):
def test_make_sales_order(self):
from erpnext.selling.doctype.quotation.quotation import make_sales_order
-
+
quotation = frappe.copy_doc(test_records[0])
quotation.insert()
-
+
self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name)
-
+
quotation.submit()
sales_order = make_sales_order(quotation.name)
-
- self.assertEquals(sales_order[0]["doctype"], "Sales Order")
- self.assertEquals(len(sales_order), 2)
- self.assertEquals(sales_order[1]["doctype"], "Sales Order Item")
- self.assertEquals(sales_order[1]["prevdoc_docname"], quotation.name)
- self.assertEquals(sales_order[0]["customer"], "_Test Customer")
-
- sales_order[0]["delivery_date"] = "2014-01-01"
- sales_order[0]["naming_series"] = "_T-Quotation-"
- sales_order[0]["transaction_date"] = "2013-05-12"
- frappe.get_doc(sales_order).insert()
+
+ self.assertEquals(sales_order.doctype, "Sales Order")
+ self.assertEquals(len(sales_order.get("sales_order_details")), 2)
+ self.assertEquals(sales_order.get("sales_order_details")[0]["doctype"], "Sales Order Item")
+ self.assertEquals(sales_order.get("sales_order_details")[0]["prevdoc_docname"], quotation.name)
+ self.assertEquals(sales_order.customer, "_Test Customer")
+
+ sales_order.delivery_date = "2014-01-01"
+ sales_order.naming_series = "_T-Quotation-"
+ sales_order.transaction_date = "2013-05-12"
+ sales_order.insert()
-test_records = frappe.get_test_records('Quotation')
\ No newline at end of file
+test_records = frappe.get_test_records('Quotation')
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index d420e96..f4b6833 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -18,28 +18,28 @@
person_tname = 'Target Detail'
partner_tname = 'Partner Target Detail'
territory_tname = 'Territory Target Detail'
-
+
def validate_mandatory(self):
# validate transaction date v/s delivery date
if self.delivery_date:
if getdate(self.transaction_date) > getdate(self.delivery_date):
msgprint("Expected Delivery Date cannot be before Sales Order Date")
raise Exception
-
+
def validate_po(self):
# validate p.o date v/s delivery date
if self.po_date and self.delivery_date and getdate(self.po_date) > getdate(self.delivery_date):
msgprint("Expected Delivery Date cannot be before Purchase Order Date")
- raise Exception
-
+ raise Exception
+
if self.po_no and self.customer:
so = frappe.db.sql("select name from `tabSales Order` \
where ifnull(po_no, '') = %s and name != %s and docstatus < 2\
and customer = %s", (self.po_no, self.name, self.customer))
if so and so[0][0]:
- msgprint("""Another Sales Order (%s) exists against same PO No and Customer.
+ msgprint("""Another Sales Order (%s) exists against same PO No and Customer.
Please be sure, you are not making duplicate entry.""" % so[0][0])
-
+
def validate_for_items(self):
check_list, flag = [], 0
chk_dupl_itm = []
@@ -49,9 +49,9 @@
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 'Yes':
if not d.warehouse:
- msgprint("""Please enter Reserved Warehouse for item %s
+ msgprint("""Please enter Reserved Warehouse for item %s
as it is stock Item""" % d.item_code, raise_exception=1)
-
+
if e in check_list:
msgprint("Item %s has been entered twice." % d.item_code)
else:
@@ -64,7 +64,7 @@
# used for production plan
d.transaction_date = self.transaction_date
-
+
tot_avail_qty = frappe.db.sql("select projected_qty from `tabBin` \
where item_code = %s and warehouse = %s", (d.item_code,d.warehouse))
d.projected_qty = tot_avail_qty and flt(tot_avail_qty[0][0]) or 0
@@ -79,26 +79,26 @@
def validate_order_type(self):
super(SalesOrder, self).validate_order_type()
-
+
def validate_delivery_date(self):
if self.order_type == 'Sales' and not self.delivery_date:
msgprint("Please enter 'Expected Delivery Date'")
raise Exception
-
+
self.validate_sales_mntc_quotation()
def validate_proj_cust(self):
if self.project_name and self.customer_name:
- res = frappe.db.sql("""select name from `tabProject` where name = %s
- and (customer = %s or ifnull(customer,'')='')""",
+ res = frappe.db.sql("""select name from `tabProject` where name = %s
+ and (customer = %s or ifnull(customer,'')='')""",
(self.project_name, self.customer))
if not res:
msgprint("Customer - %s does not belong to project - %s. \n\nIf you want to use project for multiple customers then please make customer details blank in project - %s."%(self.customer,self.project_name,self.project_name))
raise Exception
-
+
def validate(self):
super(SalesOrder, self).validate()
-
+
self.validate_order_type()
self.validate_delivery_date()
self.validate_mandatory()
@@ -111,28 +111,28 @@
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
make_packing_list(self,'sales_order_details')
-
+
self.validate_with_previous_doc()
-
+
if not self.status:
self.status = "Draft"
from erpnext.utilities import validate_status
- validate_status(self.status, ["Draft", "Submitted", "Stopped",
+ validate_status(self.status, ["Draft", "Submitted", "Stopped",
"Cancelled"])
if not self.billing_status: self.billing_status = 'Not Billed'
- if not self.delivery_status: self.delivery_status = 'Not Delivered'
-
+ if not self.delivery_status: self.delivery_status = 'Not Delivered'
+
def validate_warehouse(self):
from erpnext.stock.utils import validate_warehouse_company
-
- warehouses = list(set([d.warehouse for d in
+
+ warehouses = list(set([d.warehouse for d in
self.get(self.fname) if d.warehouse]))
-
+
for w in warehouses:
validate_warehouse_company(w, self.company)
-
+
def validate_with_previous_doc(self):
super(SalesOrder, self).validate_with_previous_doc(self.tname, {
"Quotation": {
@@ -141,31 +141,31 @@
}
})
-
+
def update_enquiry_status(self, prevdoc, flag):
enq = frappe.db.sql("select t2.prevdoc_docname from `tabQuotation` t1, `tabQuotation Item` t2 where t2.parent = t1.name and t1.name=%s", prevdoc)
if enq:
frappe.db.sql("update `tabOpportunity` set status = %s where name=%s",(flag,enq[0][0]))
- def update_prevdoc_status(self, flag):
+ def update_prevdoc_status(self, flag):
for quotation in list(set([d.prevdoc_docname for d in self.get(self.fname)])):
if quotation:
doc = frappe.get_doc("Quotation", quotation)
if doc.docstatus==2:
frappe.throw(quotation + ": " + frappe._("Quotation is cancelled."))
-
+
doc.set_status(update=True)
def on_submit(self):
self.update_stock_ledger(update_stock = 1)
self.check_credit(self.grand_total)
-
+
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.grand_total, self)
-
+
self.update_prevdoc_status('submit')
frappe.db.set(self, 'status', 'Submitted')
-
+
def on_cancel(self):
# Cannot cancel stopped SO
if self.status == 'Stopped':
@@ -173,45 +173,45 @@
raise Exception
self.check_nextdoc_docstatus()
self.update_stock_ledger(update_stock = -1)
-
+
self.update_prevdoc_status('cancel')
-
+
frappe.db.set(self, 'status', 'Cancelled')
-
+
def check_nextdoc_docstatus(self):
# Checks Delivery Note
submit_dn = frappe.db.sql("select t1.name from `tabDelivery Note` t1,`tabDelivery Note Item` t2 where t1.name = t2.parent and t2.against_sales_order = %s and t1.docstatus = 1", self.name)
if submit_dn:
msgprint("Delivery Note : " + cstr(submit_dn[0][0]) + " has been submitted against " + cstr(self.doctype) + ". Please cancel Delivery Note : " + cstr(submit_dn[0][0]) + " first and then cancel "+ cstr(self.doctype), raise_exception = 1)
-
+
# Checks Sales Invoice
- submit_rv = frappe.db.sql("""select t1.name
- from `tabSales Invoice` t1,`tabSales Invoice Item` t2
- where t1.name = t2.parent and t2.sales_order = %s and t1.docstatus = 1""",
+ submit_rv = frappe.db.sql("""select t1.name
+ from `tabSales Invoice` t1,`tabSales Invoice Item` t2
+ where t1.name = t2.parent and t2.sales_order = %s and t1.docstatus = 1""",
self.name)
if submit_rv:
msgprint("Sales Invoice : " + cstr(submit_rv[0][0]) + " has already been submitted against " +cstr(self.doctype)+ ". Please cancel Sales Invoice : "+ cstr(submit_rv[0][0]) + " first and then cancel "+ cstr(self.doctype), raise_exception = 1)
-
+
#check maintenance schedule
submit_ms = frappe.db.sql("select t1.name from `tabMaintenance Schedule` t1, `tabMaintenance Schedule Item` t2 where t2.parent=t1.name and t2.prevdoc_docname = %s and t1.docstatus = 1",self.name)
if submit_ms:
msgprint("Maintenance Schedule : " + cstr(submit_ms[0][0]) + " has already been submitted against " +cstr(self.doctype)+ ". Please cancel Maintenance Schedule : "+ cstr(submit_ms[0][0]) + " first and then cancel "+ cstr(self.doctype), raise_exception = 1)
-
+
# check maintenance visit
submit_mv = frappe.db.sql("select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent=t1.name and t2.prevdoc_docname = %s and t1.docstatus = 1",self.name)
if submit_mv:
msgprint("Maintenance Visit : " + cstr(submit_mv[0][0]) + " has already been submitted against " +cstr(self.doctype)+ ". Please cancel Maintenance Visit : " + cstr(submit_mv[0][0]) + " first and then cancel "+ cstr(self.doctype), raise_exception = 1)
-
+
# check production order
pro_order = frappe.db.sql("""select name from `tabProduction Order` where sales_order = %s and docstatus = 1""", self.name)
if pro_order:
- msgprint("""Production Order: %s exists against this sales order.
- Please cancel production order first and then cancel this sales order""" %
+ msgprint("""Production Order: %s exists against this sales order.
+ Please cancel production order first and then cancel this sales order""" %
pro_order[0][0], raise_exception=1)
def check_modified_date(self):
mod_db = frappe.db.get_value("Sales Order", self.name, "modified")
- date_diff = frappe.db.sql("select TIMEDIFF('%s', '%s')" %
+ date_diff = frappe.db.sql("select TIMEDIFF('%s', '%s')" %
( mod_db, cstr(self.modified)))
if date_diff and date_diff[0][0]:
msgprint("%s: %s has been modified after you have opened. Please Refresh"
@@ -221,7 +221,7 @@
self.check_modified_date()
self.update_stock_ledger(-1)
frappe.db.set(self, 'status', 'Stopped')
- msgprint("""%s: %s has been Stopped. To make transactions against this Sales Order
+ msgprint("""%s: %s has been Stopped. To make transactions against this Sales Order
you need to Unstop it.""" % (self.doctype, self.name))
def unstop_sales_order(self):
@@ -237,7 +237,7 @@
if frappe.db.get_value("Item", d['item_code'], "is_stock_item") == "Yes":
args = {
"item_code": d['item_code'],
- "warehouse": d['reserved_warehouse'],
+ "warehouse": d['reserved_warehouse'],
"reserved_qty": flt(update_stock) * flt(d['reserved_qty']),
"posting_date": self.transaction_date,
"voucher_type": self.doctype,
@@ -248,76 +248,76 @@
def on_update(self):
pass
-
+
def get_portal_page(self):
return "order" if self.docstatus==1 else None
-
+
def set_missing_values(source, target):
doc = frappe.get_doc(target)
doc.run_method("onload_post_render")
-
+
@frappe.whitelist()
-def make_material_request(source_name, target_doc=None):
- def postprocess(source, doclist):
- doclist[0].material_request_type = "Purchase"
-
- doclist = get_mapped_doc("Sales Order", source_name, {
+def make_material_request(source_name, target_doc=None):
+ def postprocess(source, doc):
+ doc.material_request_type = "Purchase"
+
+ doc = get_mapped_doc("Sales Order", source_name, {
"Sales Order": {
- "doctype": "Material Request",
+ "doctype": "Material Request",
"validation": {
"docstatus": ["=", 1]
}
- },
+ },
"Sales Order Item": {
- "doctype": "Material Request Item",
+ "doctype": "Material Request Item",
"field_map": {
- "parent": "sales_order_no",
+ "parent": "sales_order_no",
"stock_uom": "uom"
}
}
}, target_doc, postprocess)
-
- return doclist
+
+ return doc
@frappe.whitelist()
-def make_delivery_note(source_name, target_doc=None):
+def make_delivery_note(source_name, target_doc=None):
def update_item(obj, target, source_parent):
target.base_amount = (flt(obj.qty) - flt(obj.delivered_qty)) * flt(obj.base_rate)
target.amount = (flt(obj.qty) - flt(obj.delivered_qty)) * flt(obj.rate)
target.qty = flt(obj.qty) - flt(obj.delivered_qty)
-
+
doclist = get_mapped_doc("Sales Order", source_name, {
"Sales Order": {
- "doctype": "Delivery Note",
+ "doctype": "Delivery Note",
"field_map": {
- "shipping_address": "address_display",
- "shipping_address_name": "customer_address",
+ "shipping_address": "address_display",
+ "shipping_address_name": "customer_address",
},
"validation": {
"docstatus": ["=", 1]
}
- },
+ },
"Sales Order Item": {
- "doctype": "Delivery Note Item",
+ "doctype": "Delivery Note Item",
"field_map": {
- "rate": "rate",
- "name": "prevdoc_detail_docname",
- "parent": "against_sales_order",
+ "rate": "rate",
+ "name": "prevdoc_detail_docname",
+ "parent": "against_sales_order",
},
"postprocess": update_item,
"condition": lambda doc: doc.delivered_qty < doc.qty
- },
+ },
"Sales Taxes and Charges": {
- "doctype": "Sales Taxes and Charges",
+ "doctype": "Sales Taxes and Charges",
"add_if_empty": True
- },
+ },
"Sales Team": {
"doctype": "Sales Team",
"add_if_empty": True
}
}, target_doc, set_missing_values)
-
- return doclist.as_dict()
+
+ return doclist
@frappe.whitelist()
def make_sales_invoice(source_name, target_doc=None):
@@ -325,94 +325,94 @@
doc = frappe.get_doc(target)
doc.is_pos = 0
doc.run_method("onload_post_render")
-
+
def update_item(obj, target, source_parent):
target.amount = flt(obj.amount) - flt(obj.billed_amt)
target.base_amount = target.amount * flt(source_parent.conversion_rate)
target.qty = obj.rate and target.amount / flt(obj.rate) or obj.qty
-
+
doclist = get_mapped_doc("Sales Order", source_name, {
"Sales Order": {
- "doctype": "Sales Invoice",
+ "doctype": "Sales Invoice",
"validation": {
"docstatus": ["=", 1]
}
- },
+ },
"Sales Order Item": {
- "doctype": "Sales Invoice Item",
+ "doctype": "Sales Invoice Item",
"field_map": {
- "name": "so_detail",
- "parent": "sales_order",
+ "name": "so_detail",
+ "parent": "sales_order",
},
"postprocess": update_item,
"condition": lambda doc: doc.base_amount==0 or doc.billed_amt < doc.amount
- },
+ },
"Sales Taxes and Charges": {
- "doctype": "Sales Taxes and Charges",
+ "doctype": "Sales Taxes and Charges",
"add_if_empty": True
- },
+ },
"Sales Team": {
- "doctype": "Sales Team",
+ "doctype": "Sales Team",
"add_if_empty": True
}
}, target_doc, set_missing_values)
-
- return doclist.as_dict()
-
+
+ return doclist
+
@frappe.whitelist()
def make_maintenance_schedule(source_name, target_doc=None):
- maint_schedule = frappe.db.sql("""select t1.name
- from `tabMaintenance Schedule` t1, `tabMaintenance Schedule Item` t2
+ maint_schedule = frappe.db.sql("""select t1.name
+ from `tabMaintenance Schedule` t1, `tabMaintenance Schedule Item` t2
where t2.parent=t1.name and t2.prevdoc_docname=%s and t1.docstatus=1""", source_name)
-
+
if not maint_schedule:
doclist = get_mapped_doc("Sales Order", source_name, {
"Sales Order": {
- "doctype": "Maintenance Schedule",
+ "doctype": "Maintenance Schedule",
"field_map": {
"name": "sales_order_no"
- },
+ },
"validation": {
"docstatus": ["=", 1]
}
- },
+ },
"Sales Order Item": {
- "doctype": "Maintenance Schedule Item",
+ "doctype": "Maintenance Schedule Item",
"field_map": {
"parent": "prevdoc_docname"
},
"add_if_empty": True
}
}, target_doc)
-
- return doclist.as_dict()
-
+
+ return doclist
+
@frappe.whitelist()
def make_maintenance_visit(source_name, target_doc=None):
- visit = frappe.db.sql("""select t1.name
- from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2
- where t2.parent=t1.name and t2.prevdoc_docname=%s
+ visit = frappe.db.sql("""select t1.name
+ from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2
+ where t2.parent=t1.name and t2.prevdoc_docname=%s
and t1.docstatus=1 and t1.completion_status='Fully Completed'""", source_name)
-
+
if not visit:
doclist = get_mapped_doc("Sales Order", source_name, {
"Sales Order": {
- "doctype": "Maintenance Visit",
+ "doctype": "Maintenance Visit",
"field_map": {
"name": "sales_order_no"
},
"validation": {
"docstatus": ["=", 1]
}
- },
+ },
"Sales Order Item": {
- "doctype": "Maintenance Visit Purpose",
+ "doctype": "Maintenance Visit Purpose",
"field_map": {
- "parent": "prevdoc_docname",
+ "parent": "prevdoc_docname",
"parenttype": "prevdoc_doctype"
},
"add_if_empty": True
}
}, target_doc)
-
- return doclist.as_dict()
+
+ return doclist
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index af52028..b41027b 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -4,82 +4,82 @@
import frappe
from frappe.utils import flt
import unittest
+import copy
class TestSalesOrder(unittest.TestCase):
def tearDown(self):
frappe.set_user("Administrator")
-
+
def test_make_material_request(self):
from erpnext.selling.doctype.sales_order.sales_order import make_material_request
-
+
so = frappe.copy_doc(test_records[0]).insert()
-
- self.assertRaises(frappe.ValidationError, make_material_request,
+
+ self.assertRaises(frappe.ValidationError, make_material_request,
so.name)
sales_order = frappe.get_doc("Sales Order", so.name)
sales_order.submit()
mr = make_material_request(so.name)
-
- self.assertEquals(mr[0]["material_request_type"], "Purchase")
- self.assertEquals(len(mr), len(sales_order))
+
+ self.assertEquals(mr.material_request_type, "Purchase")
+ self.assertEquals(len(mr.get("indent_details")), len(sales_order.get("sales_order_details")))
def test_make_delivery_note(self):
from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note
so = frappe.copy_doc(test_records[0]).insert()
- self.assertRaises(frappe.ValidationError, make_delivery_note,
+ self.assertRaises(frappe.ValidationError, make_delivery_note,
so.name)
sales_order = frappe.get_doc("Sales Order", so.name)
sales_order.submit()
dn = make_delivery_note(so.name)
-
- self.assertEquals(dn[0]["doctype"], "Delivery Note")
- self.assertEquals(len(dn), len(sales_order))
+
+ self.assertEquals(dn.doctype, "Delivery Note")
+ self.assertEquals(len(dn.get("delivery_note_details")), len(sales_order.get("sales_order_details")))
def test_make_sales_invoice(self):
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
so = frappe.copy_doc(test_records[0]).insert()
- self.assertRaises(frappe.ValidationError, make_sales_invoice,
+ self.assertRaises(frappe.ValidationError, make_sales_invoice,
so.name)
sales_order = frappe.get_doc("Sales Order", so.name)
sales_order.submit()
si = make_sales_invoice(so.name)
-
- self.assertEquals(si[0]["doctype"], "Sales Invoice")
- self.assertEquals(len(si), len(sales_order))
- self.assertEquals(len([d for d in si if d["doctype"]=="Sales Invoice Item"]), 1)
-
- si = frappe.get_doc(si)
+
+ self.assertEquals(si.doctype, "Sales Invoice")
+ self.assertEquals(len(si.get("entries")), len(sales_order.get("sales_order_details")))
+ self.assertEquals(len(si.get("entries")), 1)
+
si.posting_date = "2013-10-10"
si.insert()
si.submit()
si1 = make_sales_invoice(so.name)
- self.assertEquals(len([d for d in si1 if d["doctype"]=="Sales Invoice Item"]), 0)
-
+ self.assertEquals(len(si1.get("entries")), 0)
+
def create_so(self, so_doc = None):
if not so_doc:
so_doc = test_records[0]
-
+
w = frappe.copy_doc(so_doc)
w.insert()
w.submit()
return w
-
+
def create_dn_against_so(self, so, delivered_qty=0):
from erpnext.stock.doctype.delivery_note.test_delivery_note import test_records as dn_test_records
from erpnext.stock.doctype.delivery_note.test_delivery_note import _insert_purchase_receipt
_insert_purchase_receipt(so.get("sales_order_details")[0].item_code)
-
+
dn = frappe.get_doc(frappe.copy_doc(dn_test_records[0]))
dn.get("delivery_note_details")[0].item_code = so.get("sales_order_details")[0].item_code
dn.get("delivery_note_details")[0].against_sales_order = so.name
@@ -89,76 +89,79 @@
dn.insert()
dn.submit()
return dn
-
+
def get_bin_reserved_qty(self, item_code, warehouse):
- return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse},
+ return flt(frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse},
"reserved_qty"))
-
+
def delete_bin(self, item_code, warehouse):
- bin = frappe.db.exists({"doctype": "Bin", "item_code": item_code,
+ bin = frappe.db.exists({"doctype": "Bin", "item_code": item_code,
"warehouse": warehouse})
if bin:
frappe.delete_doc("Bin", bin[0][0])
-
+
def check_reserved_qty(self, item_code, warehouse, qty):
bin_reserved_qty = self.get_bin_reserved_qty(item_code, warehouse)
self.assertEqual(bin_reserved_qty, qty)
-
+
def test_reserved_qty_for_so(self):
# reset bin
- self.delete_bin(test_records[0][1]["item_code"], test_records[0][1]["warehouse"])
-
+ so_item = test_records[0]["sales_order_details"][0]
+ self.delete_bin(so_item["item_code"], so_item["warehouse"])
+
# submit
so = self.create_so()
self.check_reserved_qty(so.get("sales_order_details")[0].item_code, so.get("sales_order_details")[0].warehouse, 10.0)
-
+
# cancel
so.cancel()
self.check_reserved_qty(so.get("sales_order_details")[0].item_code, so.get("sales_order_details")[0].warehouse, 0.0)
-
-
+
+
def test_reserved_qty_for_partial_delivery(self):
# reset bin
- self.delete_bin(test_records[0][1]["item_code"], test_records[0][1]["warehouse"])
-
+ so_item = test_records[0]["sales_order_details"][0]
+ self.delete_bin(so_item["item_code"], so_item["warehouse"])
+
# submit so
so = self.create_so()
-
+
# allow negative stock
frappe.db.set_default("allow_negative_stock", 1)
-
+
# submit dn
dn = self.create_dn_against_so(so)
-
+
self.check_reserved_qty(so.get("sales_order_details")[0].item_code, so.get("sales_order_details")[0].warehouse, 5.0)
-
+
# stop so
so.load_from_db()
- so.obj.stop_sales_order()
+ so.stop_sales_order()
self.check_reserved_qty(so.get("sales_order_details")[0].item_code, so.get("sales_order_details")[0].warehouse, 0.0)
-
+
# unstop so
so.load_from_db()
- so.obj.unstop_sales_order()
+ so.unstop_sales_order()
self.check_reserved_qty(so.get("sales_order_details")[0].item_code, so.get("sales_order_details")[0].warehouse, 5.0)
-
+
# cancel dn
dn.cancel()
self.check_reserved_qty(so.get("sales_order_details")[0].item_code, so.get("sales_order_details")[0].warehouse, 10.0)
-
+
def test_reserved_qty_for_over_delivery(self):
# reset bin
- self.delete_bin(test_records[0][1]["item_code"], test_records[0][1]["warehouse"])
-
+ so_item = test_records[0]["sales_order_details"][0]
+ self.delete_bin(so_item["item_code"], so_item["warehouse"])
+
# submit so
so = self.create_so()
-
+
# allow negative stock
frappe.db.set_default("allow_negative_stock", 1)
-
+
# set over-delivery tolerance
frappe.db.set_value('Item', so.get("sales_order_details")[0].item_code, 'tolerance', 50)
-
+
# submit dn
dn = self.create_dn_against_so(so, 15)
self.check_reserved_qty(so.get("sales_order_details")[0].item_code, so.get("sales_order_details")[0].warehouse, 0.0)
@@ -166,127 +169,127 @@
# cancel dn
dn.cancel()
self.check_reserved_qty(so.get("sales_order_details")[0].item_code, so.get("sales_order_details")[0].warehouse, 10.0)
-
+
def test_reserved_qty_for_so_with_packing_list(self):
from erpnext.selling.doctype.sales_bom.test_sales_bom import test_records as sbom_test_records
-
+
# change item in test so record
- test_record = test_records[0][:]
- test_record[1]["item_code"] = "_Test Sales BOM Item"
-
+ test_record = copy.deepcopy(test_records[0])
+ test_record["sales_order_details"][0]["item_code"] = "_Test Sales BOM Item"
+
# reset bin
- self.delete_bin(sbom_test_records[0][1]["item_code"], test_record[1]["warehouse"])
- self.delete_bin(sbom_test_records[0][2]["item_code"], test_record[1]["warehouse"])
-
+ self.delete_bin(sbom_test_records[0]["sales_bom_items"][0]["item_code"], test_record.get("sales_order_details")[0]["warehouse"])
+ self.delete_bin(sbom_test_records[0]["sales_bom_items"][1]["item_code"], test_record.get("sales_order_details")[0]["warehouse"])
+
# submit
so = self.create_so(test_record)
-
-
- self.check_reserved_qty(sbom_test_records[0][1]["item_code"],
+
+
+ self.check_reserved_qty(sbom_test_records[0]["sales_bom_items"][0]["item_code"],
so.get("sales_order_details")[0].warehouse, 50.0)
- self.check_reserved_qty(sbom_test_records[0][2]["item_code"],
+ self.check_reserved_qty(sbom_test_records[0]["sales_bom_items"][1]["item_code"],
so.get("sales_order_details")[0].warehouse, 20.0)
-
+
# cancel
so.cancel()
- self.check_reserved_qty(sbom_test_records[0][1]["item_code"],
+ self.check_reserved_qty(sbom_test_records[0]["sales_bom_items"][0]["item_code"],
so.get("sales_order_details")[0].warehouse, 0.0)
- self.check_reserved_qty(sbom_test_records[0][2]["item_code"],
+ self.check_reserved_qty(sbom_test_records[0]["sales_bom_items"][1]["item_code"],
so.get("sales_order_details")[0].warehouse, 0.0)
-
+
def test_reserved_qty_for_partial_delivery_with_packing_list(self):
from erpnext.selling.doctype.sales_bom.test_sales_bom import test_records as sbom_test_records
-
+
# change item in test so record
-
+
test_record = frappe.copy_doc(test_records[0])
- test_record[1]["item_code"] = "_Test Sales BOM Item"
+ test_record.get("sales_order_details")[0].item_code = "_Test Sales BOM Item"
# reset bin
- self.delete_bin(sbom_test_records[0][1]["item_code"], test_record[1]["warehouse"])
- self.delete_bin(sbom_test_records[0][2]["item_code"], test_record[1]["warehouse"])
-
+ self.delete_bin(sbom_test_records[0]["sales_bom_items"][0]["item_code"], test_record.get("sales_order_details")[0].warehouse)
+ self.delete_bin(sbom_test_records[0]["sales_bom_items"][1]["item_code"], test_record.get("sales_order_details")[0].warehouse)
+
# submit
so = self.create_so(test_record)
-
+
# allow negative stock
frappe.db.set_default("allow_negative_stock", 1)
-
+
# submit dn
dn = self.create_dn_against_so(so)
-
- self.check_reserved_qty(sbom_test_records[0][1]["item_code"],
+
+ self.check_reserved_qty(sbom_test_records[0]["sales_bom_items"][0]["item_code"],
so.get("sales_order_details")[0].warehouse, 25.0)
- self.check_reserved_qty(sbom_test_records[0][2]["item_code"],
+ self.check_reserved_qty(sbom_test_records[0]["sales_bom_items"][1]["item_code"],
so.get("sales_order_details")[0].warehouse, 10.0)
-
+
# stop so
so.load_from_db()
- so.obj.stop_sales_order()
-
- self.check_reserved_qty(sbom_test_records[0][1]["item_code"],
+ so.stop_sales_order()
+
+ self.check_reserved_qty(sbom_test_records[0]["sales_bom_items"][0]["item_code"],
so.get("sales_order_details")[0].warehouse, 0.0)
- self.check_reserved_qty(sbom_test_records[0][2]["item_code"],
+ self.check_reserved_qty(sbom_test_records[0]["sales_bom_items"][1]["item_code"],
so.get("sales_order_details")[0].warehouse, 0.0)
-
+
# unstop so
so.load_from_db()
- so.obj.unstop_sales_order()
- self.check_reserved_qty(sbom_test_records[0][1]["item_code"],
+ so.unstop_sales_order()
+ self.check_reserved_qty(sbom_test_records[0]["sales_bom_items"][0]["item_code"],
so.get("sales_order_details")[0].warehouse, 25.0)
- self.check_reserved_qty(sbom_test_records[0][2]["item_code"],
+ self.check_reserved_qty(sbom_test_records[0]["sales_bom_items"][1]["item_code"],
so.get("sales_order_details")[0].warehouse, 10.0)
-
+
# cancel dn
dn.cancel()
- self.check_reserved_qty(sbom_test_records[0][1]["item_code"],
+ self.check_reserved_qty(sbom_test_records[0]["sales_bom_items"][0]["item_code"],
so.get("sales_order_details")[0].warehouse, 50.0)
- self.check_reserved_qty(sbom_test_records[0][2]["item_code"],
+ self.check_reserved_qty(sbom_test_records[0]["sales_bom_items"][1]["item_code"],
so.get("sales_order_details")[0].warehouse, 20.0)
-
+
def test_reserved_qty_for_over_delivery_with_packing_list(self):
from erpnext.selling.doctype.sales_bom.test_sales_bom import test_records as sbom_test_records
-
+
# change item in test so record
test_record = frappe.copy_doc(test_records[0])
- test_record[1]["item_code"] = "_Test Sales BOM Item"
+ test_record.get("sales_order_details")[0].item_code = "_Test Sales BOM Item"
# reset bin
- self.delete_bin(sbom_test_records[0][1]["item_code"], test_record[1]["warehouse"])
- self.delete_bin(sbom_test_records[0][2]["item_code"], test_record[1]["warehouse"])
-
+ self.delete_bin(sbom_test_records[0]["sales_bom_items"][0]["item_code"], test_record.get("sales_order_details")[0].warehouse)
+ self.delete_bin(sbom_test_records[0]["sales_bom_items"][1]["item_code"], test_record.get("sales_order_details")[0].warehouse)
+
# submit
so = self.create_so(test_record)
-
+
# allow negative stock
frappe.db.set_default("allow_negative_stock", 1)
-
+
# set over-delivery tolerance
frappe.db.set_value('Item', so.get("sales_order_details")[0].item_code, 'tolerance', 50)
-
+
# submit dn
dn = self.create_dn_against_so(so, 15)
-
- self.check_reserved_qty(sbom_test_records[0][1]["item_code"],
+
+ self.check_reserved_qty(sbom_test_records[0]["sales_bom_items"][0]["item_code"],
so.get("sales_order_details")[0].warehouse, 0.0)
- self.check_reserved_qty(sbom_test_records[0][2]["item_code"],
+ self.check_reserved_qty(sbom_test_records[0]["sales_bom_items"][1]["item_code"],
so.get("sales_order_details")[0].warehouse, 0.0)
# cancel dn
dn.cancel()
- self.check_reserved_qty(sbom_test_records[0][1]["item_code"],
+ self.check_reserved_qty(sbom_test_records[0]["sales_bom_items"][0]["item_code"],
so.get("sales_order_details")[0].warehouse, 50.0)
- self.check_reserved_qty(sbom_test_records[0][2]["item_code"],
+ self.check_reserved_qty(sbom_test_records[0]["sales_bom_items"][1]["item_code"],
so.get("sales_order_details")[0].warehouse, 20.0)
def test_warehouse_user(self):
frappe.defaults.add_default("Warehouse", "_Test Warehouse 1 - _TC1", "test@example.com", "Restriction")
frappe.get_doc("User", "test@example.com")\
.add_roles("Sales User", "Sales Manager", "Material User", "Material Manager")
-
+
frappe.get_doc("User", "test2@example.com")\
.add_roles("Sales User", "Sales Manager", "Material User", "Material Manager")
-
+
frappe.set_user("test@example.com")
so = frappe.copy_doc(test_records[0])
@@ -298,9 +301,9 @@
frappe.set_user("test2@example.com")
so.insert()
-
+
frappe.defaults.clear_default("Warehouse", "_Test Warehouse 1 - _TC1", "test@example.com", parenttype="Restriction")
test_dependencies = ["Sales BOM", "Currency Exchange"]
-
-test_records = frappe.get_test_records('Sales Order')
\ No newline at end of file
+
+test_records = frappe.get_test_records('Sales Order')
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 347b1f4..ba1509f 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -28,21 +28,21 @@
'status_field': 'delivery_status',
'keyword': 'Delivered'
}]
-
+
def onload(self):
billed_qty = frappe.db.sql("""select sum(ifnull(qty, 0)) from `tabSales Invoice Item`
where docstatus=1 and delivery_note=%s""", self.name)
if billed_qty:
total_qty = sum((item.qty for item in self.get("delivery_note_details")))
self.set("__billing_complete", billed_qty[0][0] == total_qty)
-
+
def get_portal_page(self):
return "shipment" if self.docstatus==1 else None
def set_actual_qty(self):
for d in self.get('delivery_note_details'):
if d.item_code and d.warehouse:
- actual_qty = frappe.db.sql("""select actual_qty from `tabBin`
+ actual_qty = frappe.db.sql("""select actual_qty from `tabBin`
where item_code = %s and warehouse = %s""", (d.item_code, d.warehouse))
d.actual_qty = actual_qty and flt(actual_qty[0][0]) or 0
@@ -57,7 +57,7 @@
def validate(self):
super(DeliveryNote, self).validate()
-
+
from erpnext.utilities import validate_status
validate_status(self.status, ["Draft", "Submitted", "Cancelled"])
@@ -67,18 +67,18 @@
self.validate_for_items()
self.validate_warehouse()
self.validate_uom_is_integer("stock_uom", "qty")
- self.update_current_stock()
+ self.update_current_stock()
self.validate_with_previous_doc()
-
+
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
make_packing_list(self, 'delivery_note_details')
-
+
self.status = 'Draft'
- if not self.installation_status: self.installation_status = 'Not Installed'
-
+ if not self.installation_status: self.installation_status = 'Not Installed'
+
def validate_with_previous_doc(self):
items = self.get("delivery_note_details")
-
+
for fn in (("Sales Order", "against_sales_order"), ("Sales Invoice", "against_sales_invoice")):
if filter(None, [getattr(d, fn[1], None) for d in items]):
super(DeliveryNote, self).validate_with_previous_doc(self.tname, {
@@ -97,12 +97,12 @@
"is_child_table": True
}
})
-
+
def validate_proj_cust(self):
"""check for does customer belong to same project as entered.."""
if self.project_name and self.customer:
- res = frappe.db.sql("""select name from `tabProject`
- where name = %s and (customer = %s or
+ res = frappe.db.sql("""select name from `tabProject`
+ where name = %s and (customer = %s or
ifnull(customer,'')='')""", (self.project_name, self.customer))
if not res:
msgprint("Customer - %s does not belong to project - %s. \n\nIf you want to use project for multiple customers then please make customer details blank in project - %s."%(self.customer,self.project_name,self.project_name))
@@ -116,13 +116,13 @@
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 'Yes':
if e in check_list:
- msgprint("Please check whether item %s has been entered twice wrongly."
+ msgprint("Please check whether item %s has been entered twice wrongly."
% d.item_code)
else:
check_list.append(e)
else:
if f in chk_dupl_itm:
- msgprint("Please check whether item %s has been entered twice wrongly."
+ msgprint("Please check whether item %s has been entered twice wrongly."
% d.item_code)
else:
chk_dupl_itm.append(f)
@@ -133,7 +133,7 @@
if not d['warehouse']:
msgprint("Please enter Warehouse for item %s as it is stock item"
% d['item_code'], raise_exception=1)
-
+
def update_current_stock(self):
for d in self.get('delivery_note_details'):
@@ -150,15 +150,15 @@
# Check for Approving Authority
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.company, self.grand_total, self)
-
- # update delivered qty in sales order
+
+ # update delivered qty in sales order
self.update_prevdoc_status()
-
+
# create stock ledger entry
self.update_stock_ledger()
self.credit_limit()
-
+
self.make_gl_entries()
# set DN status
@@ -168,14 +168,14 @@
def on_cancel(self):
self.check_stop_sales_order("against_sales_order")
self.check_next_docstatus()
-
+
self.update_prevdoc_status()
-
+
self.update_stock_ledger()
frappe.db.set(self, 'status', 'Cancelled')
self.cancel_packing_slips()
-
+
self.make_cancel_gl_entries()
def validate_packed_qty(self):
@@ -198,17 +198,17 @@
frappe.msgprint("Packing Error:\n" + err_msg, raise_exception=1)
def check_next_docstatus(self):
- submit_rv = frappe.db.sql("""select t1.name
- from `tabSales Invoice` t1,`tabSales Invoice Item` t2
- where t1.name = t2.parent and t2.delivery_note = %s and t1.docstatus = 1""",
+ submit_rv = frappe.db.sql("""select t1.name
+ from `tabSales Invoice` t1,`tabSales Invoice Item` t2
+ where t1.name = t2.parent and t2.delivery_note = %s and t1.docstatus = 1""",
(self.name))
if submit_rv:
msgprint("Sales Invoice : " + cstr(submit_rv[0][0]) + " has already been submitted !")
raise Exception , "Validation Error."
- submit_in = frappe.db.sql("""select t1.name
- from `tabInstallation Note` t1, `tabInstallation Note Item` t2
- where t1.name = t2.parent and t2.prevdoc_docname = %s and t1.docstatus = 1""",
+ submit_in = frappe.db.sql("""select t1.name
+ from `tabInstallation Note` t1, `tabInstallation Note Item` t2
+ where t1.name = t2.parent and t2.prevdoc_docname = %s and t1.docstatus = 1""",
(self.name))
if submit_in:
msgprint("Installation Note : "+cstr(submit_in[0][0]) +" has already been submitted !")
@@ -218,7 +218,7 @@
"""
Cancel submitted packing slips related to this delivery note
"""
- res = frappe.db.sql("""SELECT name FROM `tabPacking Slip` WHERE delivery_note = %s
+ res = frappe.db.sql("""SELECT name FROM `tabPacking Slip` WHERE delivery_note = %s
AND docstatus = 1""", self.name)
if res:
@@ -234,19 +234,19 @@
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" \
and d.warehouse:
self.update_reserved_qty(d)
-
+
sl_entries.append(self.get_sl_entries(d, {
"actual_qty": -1*flt(d['qty']),
}))
-
+
self.make_sl_entries(sl_entries)
-
+
def update_reserved_qty(self, d):
if d['reserved_qty'] < 0 :
# Reduce reserved qty from reserved warehouse mentioned in so
if not d["reserved_warehouse"]:
frappe.throw(_("Reserved Warehouse is missing in Sales Order"))
-
+
args = {
"item_code": d['item_code'],
"warehouse": d["reserved_warehouse"],
@@ -271,88 +271,88 @@
def get_invoiced_qty_map(delivery_note):
"""returns a map: {dn_detail: invoiced_qty}"""
invoiced_qty_map = {}
-
+
for dn_detail, qty in frappe.db.sql("""select dn_detail, qty from `tabSales Invoice Item`
where delivery_note=%s and docstatus=1""", delivery_note):
if not invoiced_qty_map.get(dn_detail):
invoiced_qty_map[dn_detail] = 0
invoiced_qty_map[dn_detail] += qty
-
+
return invoiced_qty_map
@frappe.whitelist()
def make_sales_invoice(source_name, target_doc=None):
invoiced_qty_map = get_invoiced_qty_map(source_name)
-
+
def update_accounts(source, target):
si = frappe.get_doc(target)
si.is_pos = 0
si.run_method("onload_post_render")
-
+
if len(si.get("entries")) == 0:
frappe.msgprint(_("All these items have already been invoiced."),
raise_exception=True)
-
+
def update_item(source_doc, target_doc, source_parent):
target_doc.qty = source_doc.qty - invoiced_qty_map.get(source_doc.name, 0)
-
+
doc = get_mapped_doc("Delivery Note", source_name, {
"Delivery Note": {
- "doctype": "Sales Invoice",
+ "doctype": "Sales Invoice",
"validation": {
"docstatus": ["=", 1]
}
- },
+ },
"Delivery Note Item": {
- "doctype": "Sales Invoice Item",
+ "doctype": "Sales Invoice Item",
"field_map": {
- "name": "dn_detail",
- "parent": "delivery_note",
- "prevdoc_detail_docname": "so_detail",
- "against_sales_order": "sales_order",
+ "name": "dn_detail",
+ "parent": "delivery_note",
+ "prevdoc_detail_docname": "so_detail",
+ "against_sales_order": "sales_order",
"serial_no": "serial_no"
},
"postprocess": update_item,
- "filter": lambda d: d.qty - invoiced_qty_map.get(d.name, 0)<=0
- },
+ "filter": lambda d: d.qty - invoiced_qty_map.get(d.name, 0)<=0
+ },
"Sales Taxes and Charges": {
- "doctype": "Sales Taxes and Charges",
+ "doctype": "Sales Taxes and Charges",
"add_if_empty": True
- },
+ },
"Sales Team": {
- "doctype": "Sales Team",
+ "doctype": "Sales Team",
"field_map": {
"incentives": "incentives"
},
"add_if_empty": True
}
}, target_doc, update_accounts)
-
- return doc.as_dict()
-
+
+ return doc
+
@frappe.whitelist()
def make_installation_note(source_name, target_doc=None):
def update_item(obj, target, source_parent):
target.qty = flt(obj.qty) - flt(obj.installed_qty)
target.serial_no = obj.serial_no
-
+
doclist = get_mapped_doc("Delivery Note", source_name, {
"Delivery Note": {
- "doctype": "Installation Note",
+ "doctype": "Installation Note",
"validation": {
"docstatus": ["=", 1]
}
- },
+ },
"Delivery Note Item": {
- "doctype": "Installation Note Item",
+ "doctype": "Installation Note Item",
"field_map": {
- "name": "prevdoc_detail_docname",
- "parent": "prevdoc_docname",
- "parenttype": "prevdoc_doctype",
+ "name": "prevdoc_detail_docname",
+ "parent": "prevdoc_docname",
+ "parenttype": "prevdoc_doctype",
},
"postprocess": update_item,
"condition": lambda doc: doc.installed_qty < doc.qty
}
}, target_doc)
- return doclist.as_dict()
\ No newline at end of file
+ return doclist
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index bd14784..3f67cd2 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -245,7 +245,7 @@
}
}, target_doc, set_missing_values)
- return doclist.as_dict()
+ return doclist
@frappe.whitelist()
def make_purchase_order_based_on_supplier(source_name, target_doc=None):
@@ -325,7 +325,7 @@
}
}, target_doc, set_missing_values)
- return doclist.as_dict()
+ return doclist
@frappe.whitelist()
def make_stock_entry(source_name, target_doc=None):
@@ -361,4 +361,4 @@
}
}, target_doc, set_missing_values)
- return doclist.as_dict()
+ return doclist
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 9a137a1..03250a6 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -323,4 +323,4 @@
}
}, target_doc, set_missing_values)
- return doclist.as_dict()
\ No newline at end of file
+ return doclist
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 9a28033..b6ffb83 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -234,6 +234,7 @@
def validate_finished_goods(self):
"""validation: finished good quantity should be same as manufacturing quantity"""
+ import json
for d in self.get('mtn_details'):
if d.bom_no and flt(d.transfer_qty) != flt(self.fg_completed_qty):
msgprint(_("Row #") + " %s: " % d.idx
diff --git a/erpnext/support/doctype/customer_issue/customer_issue.py b/erpnext/support/doctype/customer_issue/customer_issue.py
index f82488a..7c6e1b2 100644
--- a/erpnext/support/doctype/customer_issue/customer_issue.py
+++ b/erpnext/support/doctype/customer_issue/customer_issue.py
@@ -59,4 +59,4 @@
}
}, target_doc)
- return doclist.as_dict()
\ No newline at end of file
+ return doclist
\ No newline at end of file
diff --git a/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py
index 7178fa2..afc08e8 100644
--- a/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py
+++ b/erpnext/support/doctype/maintenance_schedule/maintenance_schedule.py
@@ -295,4 +295,4 @@
}
}, target_doc)
- return doclist.as_dict()
\ No newline at end of file
+ return doclist
\ No newline at end of file