Merge branch 'develop'

This commit is contained in:
Anand Doshi 2016-01-27 14:20:19 +05:30
commit c54e25b5c7
81 changed files with 28445 additions and 17115 deletions

View File

@ -1,2 +1,2 @@
from __future__ import unicode_literals
__version__ = '6.18.4'
__version__ = '6.19.0'

View File

@ -6,10 +6,10 @@ import frappe
from frappe import _
from frappe.utils import flt, fmt_money, getdate, formatdate
from frappe.model.document import Document
from erpnext.accounts.party import validate_party_gle_currency
from erpnext.accounts.party import validate_party_gle_currency, validate_party_frozen_disabled
from erpnext.accounts.utils import get_account_currency
from erpnext.setup.doctype.company.company import get_company_currency
from erpnext.exceptions import InvalidAccountCurrency, CustomerFrozen
from erpnext.exceptions import InvalidAccountCurrency
exclude_from_linked_with = True
@ -96,11 +96,7 @@ class GLEntry(Document):
frappe.throw(_("Cost Center {0} does not belong to Company {1}").format(self.cost_center, self.company))
def validate_party(self):
if self.party_type and self.party:
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
if not frozen_accounts_modifier in frappe.get_roles():
if frappe.db.get_value(self.party_type, self.party, "is_frozen"):
frappe.throw("{0} {1} is frozen".format(self.party_type, self.party), CustomerFrozen)
validate_party_frozen_disabled(self.party_type, self.party)
def validate_currency(self):
company_currency = get_company_currency(self.company)

View File

@ -298,8 +298,11 @@ class JournalEntry(AccountsController):
def set_amounts_in_company_currency(self):
for d in self.get("accounts"):
d.debit = flt(flt(d.debit_in_account_currency)*flt(d.exchange_rate), d.precision("debit"))
d.credit = flt(flt(d.credit_in_account_currency)*flt(d.exchange_rate), d.precision("credit"))
d.debit_in_account_currency = flt(d.debit_in_account_currency, d.precision("debit_in_account_currency"))
d.credit_in_account_currency = flt(d.credit_in_account_currency, d.precision("credit_in_account_currency"))
d.debit = flt(d.debit_in_account_currency * flt(d.exchange_rate), d.precision("debit"))
d.credit = flt(d.credit_in_account_currency * flt(d.exchange_rate), d.precision("credit"))
def set_exchange_rate(self):
for d in self.get("accounts"):
@ -361,10 +364,10 @@ class JournalEntry(AccountsController):
elif frappe.db.get_value("Account", d.account, "account_type") in ["Bank", "Cash"]:
total_amount += (d.debit_in_account_currency or d.credit_in_account_currency)
bank_account_currency = d.account_currency
if not self.pay_to_recd_from:
total_amount = 0
self.set_total_amount(total_amount, bank_account_currency)
def set_total_amount(self, amt, currency):
@ -526,7 +529,7 @@ def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None, a
if not account:
account = frappe.db.get_value("Account",
{"company": company, "account_type": "Bank", "is_group": 0})
elif voucher_type=="Cash Entry":
account = frappe.db.get_value("Company", company, "default_cash_account")
if not account:
@ -645,7 +648,7 @@ def get_payment_entry(ref_doc, args):
})
bank_row = je.append("accounts")
#make it bank_details
bank_account = get_default_bank_cash_account(ref_doc.company, "Bank Entry", account=args.get("bank_account"))
if bank_account:
@ -667,7 +670,7 @@ def get_payment_entry(ref_doc, args):
je.set_amounts_in_company_currency()
je.set_total_debit_credit()
return je if args.get("journal_entry") else je.as_dict()
@frappe.whitelist()

View File

@ -1563,7 +1563,7 @@
"in_list_view": 0,
"label": "Write Off Account",
"length": 0,
"no_copy": 1,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"print_hide": 1,
@ -1588,7 +1588,7 @@
"in_list_view": 0,
"label": "Write Off Cost Center",
"length": 0,
"no_copy": 1,
"no_copy": 0,
"options": "Cost Center",
"permlevel": 0,
"print_hide": 1,
@ -2487,7 +2487,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2015-12-17 16:18:58.177334",
"modified": "2016-01-25 05:18:57.728258",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@ -263,9 +263,9 @@ class PurchaseInvoice(BuyingController):
# parent's gl entry
if self.grand_total:
# Didnot use base_grand_total to book rounding loss gle
grand_total_in_company_currency = flt(self.grand_total * self.conversion_rate,
grand_total_in_company_currency = flt(self.grand_total * self.conversion_rate,
self.precision("grand_total"))
gl_entries.append(
self.get_gl_dict({
"account": self.credit_to,
@ -454,6 +454,9 @@ class PurchaseInvoice(BuyingController):
for pr in set(updated_pr):
frappe.get_doc("Purchase Receipt", pr).update_billing_percentage(update_modified=update_modified)
def on_recurring(self, reference_doc):
self.due_date = None
@frappe.whitelist()
def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
from erpnext.controllers.queries import get_match_cond

View File

@ -532,9 +532,9 @@ class SalesInvoice(SellingController):
def make_customer_gl_entry(self, gl_entries):
if self.grand_total:
# Didnot use base_grand_total to book rounding loss gle
grand_total_in_company_currency = flt(self.grand_total * self.conversion_rate,
grand_total_in_company_currency = flt(self.grand_total * self.conversion_rate,
self.precision("grand_total"))
gl_entries.append(
self.get_gl_dict({
"account": self.debit_to,
@ -656,6 +656,12 @@ class SalesInvoice(SellingController):
for dn in set(updated_delivery_notes):
frappe.get_doc("Delivery Note", dn).update_billing_percentage(update_modified=update_modified)
def on_recurring(self, reference_doc):
for fieldname in ("c_form_applicable", "c_form_no", "write_off_amount"):
self.set(fieldname, reference_doc.get(fieldname))
self.due_date = None
def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context
list_context = get_list_context(context)

View File

@ -10,7 +10,7 @@ from frappe.defaults import get_user_permissions
from frappe.utils import add_days, getdate, formatdate, get_first_day, date_diff
from erpnext.utilities.doctype.address.address import get_address_display
from erpnext.utilities.doctype.contact.contact import get_contact_details
from erpnext.exceptions import InvalidAccountCurrency
from erpnext.exceptions import PartyFrozen, InvalidCurrency, PartyDisabled, InvalidAccountCurrency
class DuplicatePartyAccountError(frappe.ValidationError): pass
@ -237,16 +237,11 @@ def get_due_date(posting_date, party_type, party, company):
due_date = None
if posting_date and party:
due_date = posting_date
if party_type=="Customer":
credit_days_based_on, credit_days = get_credit_days(party_type, party, company)
if credit_days_based_on == "Fixed Days" and credit_days:
due_date = add_days(posting_date, credit_days)
elif credit_days_based_on == "Last Day of the Next Month":
due_date = (get_first_day(posting_date, 0, 2) + datetime.timedelta(-1)).strftime("%Y-%m-%d")
else:
credit_days = get_credit_days(party_type, party, company)
if credit_days:
due_date = add_days(posting_date, credit_days)
credit_days_based_on, credit_days = get_credit_days(party_type, party, company)
if credit_days_based_on == "Fixed Days" and credit_days:
due_date = add_days(posting_date, credit_days)
elif credit_days_based_on == "Last Day of the Next Month":
due_date = (get_first_day(posting_date, 0, 2) + datetime.timedelta(-1)).strftime("%Y-%m-%d")
return due_date
@ -255,20 +250,21 @@ def get_credit_days(party_type, party, company):
if party_type == "Customer":
credit_days_based_on, credit_days, customer_group = \
frappe.db.get_value(party_type, party, ["credit_days_based_on", "credit_days", "customer_group"])
if not credit_days_based_on:
credit_days_based_on, credit_days = \
frappe.db.get_value("Customer Group", customer_group, ["credit_days_based_on", "credit_days"]) \
or frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"])
return credit_days_based_on, credit_days
else:
credit_days, supplier_type = frappe.db.get_value(party_type, party, ["credit_days", "supplier_type"])
if not credit_days:
credit_days = frappe.db.get_value("Supplier Type", supplier_type, "credit_days") \
or frappe.db.get_value("Company", company, "credit_days")
credit_days_based_on, credit_days, supplier_type = \
frappe.db.get_value(party_type, party, ["credit_days_based_on", "credit_days", "supplier_type"])
return credit_days
if not credit_days_based_on:
if party_type == "Customer":
credit_days_based_on, credit_days = \
frappe.db.get_value("Customer Group", customer_group, ["credit_days_based_on", "credit_days"]) \
or frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"])
else:
credit_days_based_on, credit_days = \
frappe.db.get_value("Supplier Type", supplier_type, ["credit_days_based_on", "credit_days"])\
or frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"] )
return credit_days_based_on, credit_days
def validate_due_date(posting_date, due_date, party_type, party, company):
if getdate(due_date) < getdate(posting_date):
@ -312,3 +308,13 @@ def set_taxes(party, party_type, posting_date, company, customer_group=None, sup
args.update({"use_for_shopping_cart": use_for_shopping_cart})
return get_tax_template(posting_date, args)
def validate_party_frozen_disabled(party_type, party_name):
if party_type and party_name:
party = frappe.db.get_value(party_type, party_name, ["is_frozen", "disabled"], as_dict=True)
if party.disabled:
frappe.throw("{0} {1} is disabled".format(party_type, party_name), PartyDisabled)
elif party.is_frozen:
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
if not frozen_accounts_modifier in frappe.get_roles():
frappe.throw("{0} {1} is frozen".format(party_type, party_name), PartyFrozen)

View File

@ -84,11 +84,11 @@ class PurchaseOrder(BuyingController):
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):
"""get last purchase rates for all items"""
conversion_rate = flt(self.get('conversion_rate')) or 1.0
for d in self.get("items"):
@ -96,7 +96,7 @@ class PurchaseOrder(BuyingController):
last_purchase_details = get_last_purchase_details(d.item_code, self.name)
if last_purchase_details:
d.base_price_list_rate = (last_purchase_details['base_price_list_rate'] *
d.base_price_list_rate = (last_purchase_details['base_price_list_rate'] *
(flt(d.conversion_factor) or 1.0))
d.discount_percentage = last_purchase_details['discount_percentage']
d.base_rate = last_purchase_details['base_rate'] * (flt(d.conversion_factor) or 1.0)
@ -104,7 +104,7 @@ class PurchaseOrder(BuyingController):
d.rate = d.base_rate / conversion_rate
else:
# if no last purchase found, reset all values to 0
for field in ("base_price_list_rate", "base_rate",
for field in ("base_price_list_rate", "base_rate",
"price_list_rate", "rate", "discount_percentage"):
d.set(field, 0)
@ -188,7 +188,7 @@ class PurchaseOrder(BuyingController):
def on_cancel(self):
if self.is_against_so():
self.update_status_updater()
if self.has_drop_ship_item():
self.update_delivered_qty_in_sales_order()
@ -219,17 +219,6 @@ class PurchaseOrder(BuyingController):
def on_update(self):
pass
def before_recurring(self):
super(PurchaseOrder, self).before_recurring()
for field in ("per_received", "per_billed"):
self.set(field, None)
for d in self.get("items"):
for field in ("received_qty", "billed_amt", "prevdoc_doctype", "prevdoc_docname",
"prevdoc_detail_docname", "supplier_quotation", "supplier_quotation_item"):
d.set(field, None)
def update_status_updater(self):
self.status_updater[0].update({
"target_parent_dt": "Sales Order",
@ -254,7 +243,7 @@ class PurchaseOrder(BuyingController):
def has_drop_ship_item(self):
return any([d.delivered_by_supplier for d in self.items])
def is_against_so(self):
return any([d.prevdoc_doctype for d in self.items if d.prevdoc_doctype=="Sales Order"])

View File

@ -19,16 +19,16 @@ class TestPurchaseOrder(unittest.TestCase):
def test_ordered_qty(self):
existing_ordered_qty = get_ordered_qty()
po = create_purchase_order(do_not_submit=True)
self.assertRaises(frappe.ValidationError, make_purchase_receipt, po.name)
po.submit()
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 10)
create_pr_against_po(po.name)
create_pr_against_po(po.name)
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 6)
po.load_from_db()
self.assertEquals(po.get("items")[0].received_qty, 4)
@ -36,13 +36,13 @@ class TestPurchaseOrder(unittest.TestCase):
pr = create_pr_against_po(po.name, received_qty=8)
self.assertEqual(get_ordered_qty(), existing_ordered_qty)
po.load_from_db()
self.assertEquals(po.get("items")[0].received_qty, 12)
pr.cancel()
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 6)
po.load_from_db()
self.assertEquals(po.get("items")[0].received_qty, 4)
@ -70,19 +70,19 @@ class TestPurchaseOrder(unittest.TestCase):
from erpnext.utilities.transaction_base import UOMMustBeIntegerError
po = create_purchase_order(qty=3.4, do_not_save=True)
self.assertRaises(UOMMustBeIntegerError, po.insert)
def test_ordered_qty_for_closing_po(self):
bin = frappe.get_all("Bin", filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
fields=["ordered_qty"])
def test_ordered_qty_for_closing_po(self):
bin = frappe.get_all("Bin", filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"},
fields=["ordered_qty"])
existing_ordered_qty = bin[0].ordered_qty if bin else 0.0
po = create_purchase_order(item_code= "_Test Item", qty=1)
self.assertEquals(get_ordered_qty(item_code= "_Test Item", warehouse="_Test Warehouse - _TC"), existing_ordered_qty+1)
po.update_status("Closed")
self.assertEquals(get_ordered_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"), existing_ordered_qty)
def create_purchase_order(**args):
@ -108,9 +108,9 @@ def create_purchase_order(**args):
po.insert()
if not args.do_not_submit:
po.submit()
return po
def create_pr_against_po(po, received_qty=4):
pr = make_purchase_receipt(po)
pr.get("items")[0].qty = received_qty

View File

@ -7,6 +7,7 @@
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"fields": [
{
"allow_on_submit": 0,
@ -1155,7 +1156,7 @@
"in_list_view": 0,
"label": "BOM",
"length": 0,
"no_copy": 1,
"no_copy": 0,
"options": "BOM",
"permlevel": 0,
"precision": "",
@ -1330,7 +1331,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-01-06 02:21:10.407871",
"modified": "2016-01-25 05:39:52.405200",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",

File diff suppressed because it is too large Load Diff

View File

@ -3,5 +3,70 @@
from __future__ import unicode_literals
import frappe
test_records = frappe.get_test_records('Supplier')
import frappe, unittest
from erpnext.accounts.party import get_due_date
from erpnext.exceptions import PartyFrozen, PartyDisabled
from frappe.test_runner import make_test_records
test_records = frappe.get_test_records('Supplier')
class TestSupplier(unittest.TestCase):
def test_supplier_due_date_against_supplier_credit_limit(self):
# Set Credit Limit based on Fixed days
frappe.db.set_value("Supplier", "_Test Supplier", "credit_days_based_on", "Fixed Days")
frappe.db.set_value("Supplier", "_Test Supplier", "credit_days", 10)
due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier", "_Test Company")
self.assertEqual(due_date, "2016-02-01")
# Set Credit Limit based on Last day next month
frappe.db.set_value("Supplier", "_Test Supplier", "credit_days", 0)
frappe.db.set_value("Supplier", "_Test Supplier", "credit_days_based_on",
"Last Day of the Next Month")
# Leap year
due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier", "_Test Company")
self.assertEqual(due_date, "2016-02-29")
# Non Leap year
due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier", "_Test Company")
self.assertEqual(due_date, "2017-02-28")
frappe.db.set_value("Supplier", "_Test Supplier", "credit_days_based_on", "")
# Set credit limit for the supplier type instead of supplier and evaluate the due date
# based on Fixed days
frappe.db.set_value("Supplier Type", "_Test Supplier Type", "credit_days_based_on",
"Fixed Days")
frappe.db.set_value("Supplier Type", "_Test Supplier Type", "credit_days", 10)
due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier", "_Test Company")
self.assertEqual(due_date, "2016-02-01")
# Set credit limit for the supplier type instead of supplier and evaluate the due date
# based on Last day of next month
frappe.db.set_value("Supplier", "_Test Supplier Type", "credit_days", 0)
frappe.db.set_value("Supplier Type", "_Test Supplier Type", "credit_days_based_on",
"Last Day of the Next Month")
# Leap year
due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier", "_Test Company")
self.assertEqual(due_date, "2016-02-29")
# Non Leap year
due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier", "_Test Company")
self.assertEqual(due_date, "2017-02-28")
def test_supplier_disabled(self):
make_test_records("Item")
frappe.db.set_value("Supplier", "_Test Supplier", "disabled", 1)
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
po = create_purchase_order(do_not_save=True)
self.assertRaises(PartyDisabled, po.save)
frappe.db.set_value("Supplier", "_Test Supplier", "disabled", 0)
po.save()

View File

@ -0,0 +1,3 @@
- Now you can **disable** existing Customers / Suppliers
- Set *Last Day of the Next Month* as Credit Days for a Supplier / Supplier Type
- Don't map **No Copy** fields when creating recurring documents like Sales Order, Sales Invoice, Purchase Order and Purchase Invoice

View File

@ -10,8 +10,8 @@ from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year, get_ac
from erpnext.utilities.transaction_base import TransactionBase
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
from erpnext.controllers.sales_and_purchase_return import validate_return
from erpnext.accounts.party import get_party_account_currency
from erpnext.exceptions import CustomerFrozen, InvalidCurrency
from erpnext.accounts.party import get_party_account_currency, validate_party_frozen_disabled
from erpnext.exceptions import InvalidCurrency
force_item_fields = ("item_group", "barcode", "brand", "stock_uom")
@ -60,12 +60,6 @@ class AccountsController(TransactionBase):
validate_recurring_document(self)
convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date"))
def before_recurring(self):
if self.meta.get_field("fiscal_year"):
self.fiscal_year = None
if self.meta.get_field("due_date"):
self.due_date = None
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):
@ -422,24 +416,23 @@ class AccountsController(TransactionBase):
return self._abbr
def validate_party(self):
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
if frozen_accounts_modifier in frappe.get_roles():
return
party_type, party = self.get_party()
if party_type:
if frappe.db.get_value(party_type, party, "is_frozen"):
frappe.throw("{0} {1} is frozen".format(party_type, party), CustomerFrozen)
validate_party_frozen_disabled(party_type, party)
def get_party(self):
party_type = None
if self.meta.get_field("customer"):
if self.doctype in ("Opportunity", "Quotation", "Sales Order", "Delivery Note", "Sales Invoice"):
party_type = 'Customer'
elif self.meta.get_field("supplier"):
elif self.doctype in ("Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"):
party_type = 'Supplier'
elif self.meta.get_field("customer"):
party_type = "Customer"
elif self.meta.get_field("supplier"):
party_type = "Supplier"
party = self.get(party_type.lower()) if party_type else None
return party_type, party
@ -457,7 +450,9 @@ class AccountsController(TransactionBase):
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
.format(party_type, party, party_account_currency), InvalidCurrency)
# Note: not validating with gle account because we don't have the account at quotation / sales order level and we shouldn't stop someone from creating a sales invoice if sales order is already created
# Note: not validating with gle account because we don't have the account
# at quotation / sales order level and we shouldn't stop someone
# from creating a sales invoice if sales order is already created
@frappe.whitelist()
def get_tax_rate(account_head):

View File

@ -89,7 +89,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select {fields} from `tabCustomer`
where docstatus < 2
and ({key} like %(txt)s
or customer_name like %(txt)s)
or customer_name like %(txt)s) and disabled=0
{mcond}
order by
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
@ -118,7 +118,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select {field} from `tabSupplier`
where docstatus < 2
and ({key} like %(txt)s
or supplier_name like %(txt)s)
or supplier_name like %(txt)s) and disabled=0
{mcond}
order by
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
@ -216,12 +216,8 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len,
return frappe.db.sql("""select `tabDelivery Note`.name, `tabDelivery Note`.customer_name
from `tabDelivery Note`
where `tabDelivery Note`.`%(key)s` like %(txt)s and
`tabDelivery Note`.docstatus = 1 and status not in ("Stopped", "Closed") %(fcond)s and
(ifnull((select sum(qty) from `tabDelivery Note Item` where
`tabDelivery Note Item`.parent=`tabDelivery Note`.name), 0) >
ifnull((select sum(qty) from `tabSales Invoice Item` where
`tabSales Invoice Item`.docstatus = 1 and
`tabSales Invoice Item`.delivery_note=`tabDelivery Note`.name), 0))
`tabDelivery Note`.docstatus = 1 and status not in ("Stopped", "Closed") %(fcond)s
and (`tabDelivery Note`.per_billed < 100 or `tabDelivery Note`.grand_total = 0)
%(mcond)s order by `tabDelivery Note`.`%(key)s` asc
limit %(start)s, %(page_len)s""" % {
"key": searchfield,

View File

@ -47,12 +47,9 @@ def manage_recurring_documents(doctype, next_date=None, commit=True):
where %s=%s and recurring_id=%s and docstatus=1"""
% (doctype, date_field, '%s', '%s'), (next_date, recurring_id)):
try:
ref_wrapper = frappe.get_doc(doctype, ref_document)
if hasattr(ref_wrapper, "before_recurring"):
ref_wrapper.before_recurring()
new_document_wrapper = make_new_document(ref_wrapper, date_field, next_date)
send_notification(new_document_wrapper)
reference_doc = frappe.get_doc(doctype, ref_document)
new_doc = make_new_document(reference_doc, date_field, next_date)
send_notification(new_doc)
if commit:
frappe.db.commit()
except:
@ -63,8 +60,8 @@ def manage_recurring_documents(doctype, next_date=None, commit=True):
frappe.db.sql("update `tab%s` \
set is_recurring = 0 where name = %s" % (doctype, '%s'),
(ref_document))
notify_errors(ref_document, doctype, ref_wrapper.get("customer") or ref_wrapper.get("supplier"),
ref_wrapper.owner)
notify_errors(ref_document, doctype, reference_doc.get("customer") or reference_doc.get("supplier"),
reference_doc.owner)
frappe.db.commit()
exception_list.append(frappe.get_traceback())
@ -76,34 +73,42 @@ def manage_recurring_documents(doctype, next_date=None, commit=True):
exception_message = "\n\n".join([cstr(d) for d in exception_list])
frappe.throw(exception_message)
def make_new_document(ref_wrapper, date_field, posting_date):
def make_new_document(reference_doc, date_field, posting_date):
from erpnext.accounts.utils import get_fiscal_year
new_document = frappe.copy_doc(ref_wrapper)
mcount = month_map[ref_wrapper.recurring_type]
new_document = frappe.copy_doc(reference_doc, ignore_no_copy=True)
mcount = month_map[reference_doc.recurring_type]
from_date = get_next_date(ref_wrapper.from_date, mcount)
from_date = get_next_date(reference_doc.from_date, mcount)
# 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.from_date)) == cstr(ref_wrapper.from_date)) and \
(cstr(get_last_day(ref_wrapper.to_date)) == cstr(ref_wrapper.to_date)):
to_date = get_last_day(get_next_date(ref_wrapper.to_date, mcount))
if (cstr(get_first_day(reference_doc.from_date)) == cstr(reference_doc.from_date)) and \
(cstr(get_last_day(reference_doc.to_date)) == cstr(reference_doc.to_date)):
to_date = get_last_day(get_next_date(reference_doc.to_date, mcount))
else:
to_date = get_next_date(ref_wrapper.to_date, mcount)
to_date = get_next_date(reference_doc.to_date, mcount)
new_document.update({
date_field: posting_date,
"from_date": from_date,
"to_date": to_date,
"fiscal_year": get_fiscal_year(posting_date)[0],
"owner": ref_wrapper.owner,
"fiscal_year": get_fiscal_year(posting_date)[0]
})
if ref_wrapper.doctype == "Sales Order":
new_document.update({
"delivery_date": get_next_date(ref_wrapper.delivery_date, mcount,
cint(ref_wrapper.repeat_on_day_of_month))
})
# copy document fields
for fieldname in ("owner", "recurring_type", "repeat_on_day_of_month",
"recurring_id", "notification_email_address", "is_recurring", "end_date",
"title", "naming_series", "select_print_heading", "ignore_pricing_rule",
"posting_time", "remarks"):
if new_document.meta.get_field(fieldname):
new_document.set(fieldname, reference_doc.get(fieldname))
# copy item fields
for i, item in enumerate(new_document.items):
for fieldname in ("page_break",):
item.set(fieldname, reference_doc.items[i].get(fieldname))
new_document.run_method("on_recurring", reference_doc=reference_doc)
new_document.submit()

View File

@ -14,7 +14,7 @@ Content of Terms and Condition can be formatted as per your preference, and also
<img class="screenshot" alt="Terms and Conditions, Edit HTML" src="{{docs_base_url}}/assets/img/setup/print/terms-2.png">
This also allows you to use Terms and Condition master for footer, which otherwise is not availale in ERPNext as dedicated functionality. Since contents of Terms and Condition is always the last to appear in the print format, details of footer should be inserted at the end of the content, so that it actually appears as footer in the print format.
This also allows you to use Terms and Condition master for footer, which otherwise is not available in ERPNext as dedicated functionality. Since contents of Terms and Condition is always the last to appear in the print format, details of footer should be inserted at the end of the content, so that it actually appears as footer in the print format.
### 3. Select in Transaction

View File

@ -2,6 +2,7 @@ from __future__ import unicode_literals
import frappe
# accounts
class CustomerFrozen(frappe.ValidationError): pass
class PartyFrozen(frappe.ValidationError): pass
class InvalidAccountCurrency(frappe.ValidationError): pass
class InvalidCurrency(frappe.ValidationError): pass
class PartyDisabled(frappe.ValidationError):pass

View File

@ -7,7 +7,7 @@ app_publisher = "Frappe Technologies Pvt. Ltd."
app_description = """ERP made simple"""
app_icon = "icon-th"
app_color = "#e74c3c"
app_version = "6.18.4"
app_version = "6.19.0"
app_email = "info@erpnext.com"
app_license = "GNU General Public License (v3)"
source_link = "https://github.com/frappe/erpnext"

View File

@ -224,6 +224,7 @@ erpnext.patches.v6_4.email_digest_update
execute:frappe.delete_doc_if_exists("DocType", "Applicable Territory")
execute:frappe.delete_doc_if_exists("DocType", "Shopping Cart Price List")
execute:frappe.delete_doc_if_exists("DocType", "Shopping Cart Taxes and Charges Master")
erpnext.patches.v6_4.set_user_in_contact
erpnext.patches.v6_4.make_image_thumbnail #2015-10-20
erpnext.patches.v6_5.show_in_website_for_template_item
@ -242,4 +243,5 @@ erpnext.patches.v6_10.fix_delivery_status_of_drop_ship_item #2015-12-08
erpnext.patches.v5_8.tax_rule #2015-12-08
erpnext.patches.v6_12.set_overdue_tasks
erpnext.patches.v6_16.update_billing_status_in_dn_and_pr
erpnext.patches.v6_16.create_manufacturer_records
erpnext.patches.v6_16.create_manufacturer_records
execute:frappe.db.sql("update `tabPricing Rule` set title=name where title='' or title is null") #2016-01-27

View File

@ -241,13 +241,14 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "is_frozen",
"default": "0",
"fieldname": "disabled",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Is Frozen",
"label": "Disabled",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -591,7 +592,7 @@
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Customer Details",
"label": "More Information",
"length": 0,
"no_copy": 0,
"oldfieldtype": "Section Break",
@ -655,6 +656,30 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "is_frozen",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Is Frozen",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@ -794,7 +819,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-01-06 02:36:04.820353",
"modified": "2016-01-25 06:56:05.103168",
"modified_by": "Administrator",
"module": "Selling",
"name": "Customer",
@ -984,5 +1009,6 @@
"read_only": 0,
"read_only_onload": 0,
"search_fields": "customer_name,customer_group,territory",
"sort_order": "ASC",
"title_field": "customer_name"
}

View File

@ -7,7 +7,7 @@ import frappe
import unittest
from frappe.test_runner import make_test_records
from erpnext.exceptions import CustomerFrozen
from erpnext.exceptions import PartyFrozen, PartyDisabled
test_ignore = ["Price List"]
@ -68,22 +68,38 @@ class TestCustomer(unittest.TestCase):
frappe.rename_doc("Customer", "_Test Customer 1 Renamed", "_Test Customer 1")
def test_freezed_customer(self):
make_test_records("Customer")
make_test_records("Item")
frappe.db.set_value("Customer", "_Test Customer", "is_frozen", 1)
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
so = make_sales_order(do_not_save= True)
self.assertRaises(CustomerFrozen, so.save)
self.assertRaises(PartyFrozen, so.save)
frappe.db.set_value("Customer", "_Test Customer", "is_frozen", 0)
so.save()
def test_disabled_customer(self):
make_test_records("Item")
frappe.db.set_value("Customer", "_Test Customer", "disabled", 1)
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
so = make_sales_order(do_not_save=True)
self.assertRaises(PartyDisabled, so.save)
frappe.db.set_value("Customer", "_Test Customer", "disabled", 0)
so.save()
def test_duplicate_customer(self):
frappe.db.sql("delete from `tabCustomer` where customer_name='_Test Customer 1'")
if not frappe.db.get_value("Customer", "_Test Customer 1"):
test_customer_1 = frappe.get_doc({
"customer_group": "_Test Customer Group",
@ -105,4 +121,4 @@ class TestCustomer(unittest.TestCase):
self.assertEquals("_Test Customer 1", test_customer_1.name)
self.assertEquals("_Test Customer 1 - 1", duplicate_customer.name)
self.assertEquals(test_customer_1.customer_name, duplicate_customer.customer_name)
self.assertEquals(test_customer_1.customer_name, duplicate_customer.customer_name)

View File

@ -155,6 +155,7 @@ def _make_customer(source_name, ignore_permissions=False):
else:
raise
except frappe.MandatoryError:
frappe.local.message_log = []
frappe.throw(_("Please create Customer from Lead {0}").format(lead_name))
else:
return customer_name

View File

@ -10,6 +10,7 @@ from frappe import _
from frappe.model.mapper import get_mapped_doc
from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty
from frappe.desk.notifications import clear_doctype_notifications
from erpnext.controllers.recurring_document import month_map, get_next_date
from erpnext.controllers.selling_controller import SellingController
@ -288,13 +289,13 @@ class SalesOrder(SellingController):
frappe.db.set_value("Sales Order", self.name, "per_delivered", flt(delivered_qty/tot_qty) * 100,
update_modified=False)
def set_indicator(self):
"""Set indicator for portal"""
if self.per_billed < 100 and self.per_delivered < 100:
self.indicator_color = "orange"
self.indicator_title = _("Not Paid and Not Delivered")
elif self.per_billed == 100 and self.per_delivered < 100:
self.indicator_color = "orange"
self.indicator_title = _("Paid and Not Delivered")
@ -302,7 +303,12 @@ class SalesOrder(SellingController):
else:
self.indicator_color = "green"
self.indicator_title = _("Paid")
def on_recurring(self, reference_doc):
mcount = month_map[reference_doc.recurring_type]
self.set("delivery_date", get_next_date(reference_doc.delivery_date, mcount,
cint(reference_doc.repeat_on_day_of_month)))
def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context
list_context = get_list_context(context)
@ -328,17 +334,6 @@ def stop_or_unstop_sales_orders(names, status):
frappe.local.message_log = []
def before_recurring(self):
super(SalesOrder, self).before_recurring()
for field in ("delivery_status", "per_delivered", "billing_status", "per_billed"):
self.set(field, None)
for d in self.get("items"):
for field in ("delivered_qty", "billed_amt", "planned_qty", "prevdoc_docname"):
d.set(field, None)
@frappe.whitelist()
def make_material_request(source_name, target_doc=None):
def postprocess(source, doc):

View File

@ -26,6 +26,7 @@
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
@ -33,10 +34,59 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"fieldname": "section_credit_limit",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Credit Limit",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "credit_days_based_on",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Credit Days Based On",
"length": 0,
"no_copy": 0,
"options": "\nFixed Days\nLast Day of the Next Month",
"permlevel": 1,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "eval:doc.credit_days_based_on=='Fixed Days'",
"fieldname": "credit_days",
"fieldtype": "Int",
"hidden": 0,
@ -46,8 +96,10 @@
"label": "Credit Days",
"length": 0,
"no_copy": 0,
"permlevel": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@ -70,6 +122,7 @@
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@ -95,6 +148,7 @@
"options": "Party Account",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@ -113,7 +167,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2015-11-16 06:29:58.970888",
"modified": "2016-01-25 02:09:49.846022",
"modified_by": "Administrator",
"module": "Setup",
"name": "Supplier Type",
@ -241,5 +295,6 @@
}
],
"read_only": 0,
"read_only_onload": 0
"read_only_onload": 0,
"sort_order": "ASC"
}

View File

@ -88,7 +88,7 @@ class TestItem(unittest.TestCase):
"company": "_Test Company",
"price_list": "_Test Price List",
"currency": "_Test Currency",
"parenttype": "Sales Order",
"doctype": "Sales Order",
"conversion_rate": 1,
"price_list_currency": "_Test Currency",
"plc_conversion_rate": 1,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

3636
erpnext/translations/et.csv Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

3636
erpnext/translations/te.csv Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

3635
erpnext/translations/ur.csv Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
from setuptools import setup, find_packages
from pip.req import parse_requirements
version = "6.18.4"
version = "6.19.0"
requirements = parse_requirements("requirements.txt", session="")
setup(