Merge branch 'develop' into payment-terms

This commit is contained in:
tunde 2017-09-29 13:30:22 +01:00
commit c49e748c85
18 changed files with 285 additions and 74 deletions

View File

@ -4,7 +4,7 @@ import inspect
import frappe
from erpnext.hooks import regional_overrides
__version__ = '9.0.3'
__version__ = '9.0.4'
def get_default_company(user=None):
'''Get default company for user'''

View File

@ -520,6 +520,24 @@ frappe.ui.form.on('Sales Invoice', {
};
});
},
//When multiple companies are set up. in case company name is changed set default company address
company:function(frm){
if (frm.doc.company)
{
frappe.call({
method:"frappe.contacts.doctype.address.address.get_default_address",
args:{ doctype:'Company',name:frm.doc.company},
callback: function(r){
if (r.message){
frm.set_value("company_address",r.message)
}
else {
frm.set_value("company_address","")
}
}
})
}
},
project: function(frm){
frm.call({

View File

@ -324,11 +324,15 @@ def set_taxes(party, party_type, posting_date, company, customer_group=None, sup
from erpnext.accounts.doctype.tax_rule.tax_rule import get_tax_template, get_party_details
args = {
party_type.lower(): party,
"customer_group": customer_group,
"supplier_type": supplier_type,
"company": company
}
if customer_group:
args['customer_group'] = customer_group
if supplier_type:
args['supplier_type'] = supplier_type
if billing_address or shipping_address:
args.update(get_party_details(party, party_type, {"billing_address": billing_address, \
"shipping_address": shipping_address }))

View File

@ -1,3 +1,4 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
@ -24,6 +25,18 @@ frappe.ui.form.on("Purchase Order", {
},
});
frappe.ui.form.on("Purchase Order Item", {
item_code: function(frm) {
frappe.call({
method: "get_last_purchase_rate",
doc: frm.doc,
callback: function(r, rt) {
frm.trigger('calculate_taxes_and_totals');
}
})
}
});
erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({
refresh: function(doc, cdt, cdn) {
var me = this;
@ -214,17 +227,6 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
delivered_by_supplier: function(){
cur_frm.cscript.update_status('Deliver', 'Delivered')
},
get_last_purchase_rate: function() {
frappe.call({
"method": "get_last_purchase_rate",
"doc": cur_frm.doc,
callback: function(r, rt) {
cur_frm.dirty();
cur_frm.cscript.calculate_taxes_and_totals();
}
})
}
});

View File

@ -1206,37 +1206,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.docstatus===0 && (doc.items && doc.items.length)",
"fieldname": "get_last_purchase_rate",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Get last purchase rate",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -3550,8 +3519,8 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-09-26 04:41:49.329626",
"modified_by": "Administrator",
"modified": "2017-09-22 16:11:49.856808",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
"owner": "Administrator",

View File

@ -116,14 +116,13 @@ class PurchaseOrder(BuyingController):
d.discount_percentage = last_purchase_details['discount_percentage']
d.base_rate = last_purchase_details['base_rate'] * (flt(d.conversion_factor) or 1.0)
d.price_list_rate = d.base_price_list_rate / conversion_rate
d.rate = d.base_rate / conversion_rate
d.last_purchase_rate = d.base_rate / conversion_rate
else:
msgprint(_("Last purchase rate not found"))
item_last_purchase_rate = frappe.db.get_value("Item", d.item_code, "last_purchase_rate")
if item_last_purchase_rate:
d.base_price_list_rate = d.base_rate = d.price_list_rate \
= d.rate = item_last_purchase_rate
= d.last_purchase_rate = item_last_purchase_rate
# Check for Closed status
def check_for_closed_status(self):

View File

@ -0,0 +1,99 @@
QUnit.module('Buying');
QUnit.test("test: purchase order with last purchase rate", function(assert) {
assert.expect(5);
let done = assert.async();
frappe.run_serially([
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{is_subcontracted: 'No'},
{currency: 'INR'},
{items: [
[
{"item_code": 'Test Product 4'},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"qty": 1},
{"rate": 800},
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
],
[
{"item_code": 'Test Product 1'},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"qty": 1},
{"rate": 400},
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]}
]);
},
() => {
// Get item details
assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item 1 name correct");
assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Item 2 name correct");
},
() => frappe.timeout(1),
() => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'),
() => frappe.timeout(3),
() => frappe.tests.click_button('Close'),
() => frappe.timeout(1),
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{is_subcontracted: 'No'},
{currency: 'INR'},
{items: [
[
{"item_code": 'Test Product 4'},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"qty": 1},
{"rate": 600},
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
],
[
{"item_code": 'Test Product 1'},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"qty": 1},
{"rate": 200},
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]}
]);
},
() => frappe.timeout(2),
// Get the last purchase rate of items
() => {
assert.ok(cur_frm.doc.items[0].last_purchase_rate == 800, "Last purchase rate of item 1 correct");
},
() => {
assert.ok(cur_frm.doc.items[1].last_purchase_rate == 400, "Last purchase rate of item 2 correct");
},
() => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'),
() => frappe.timeout(3),
() => frappe.tests.click_button('Close'),
() => frappe.timeout(1),
() => {
assert.ok(cur_frm.doc.status == 'To Receive and Bill', "Submitted successfully");
},
() => done()
]);
});

View File

@ -655,6 +655,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "last_purchase_rate",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Last Purchase Rate",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -1714,7 +1745,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-08-02 22:15:47.411235",
"modified": "2017-09-22 16:47:08.783546",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",

View File

@ -268,5 +268,13 @@ def get_data():
"icon": "octicon octicon-plus",
"type": "module",
"label": _("Healthcare")
}
},
{
"module_name": "Data Import Tool",
"color": "#7f8c8d",
"icon": "octicon octicon-circuit-board",
"type": "page",
"link": "data-import-tool",
"label": _("Data Import Tool")
},
]

View File

@ -207,7 +207,7 @@ def copy_attributes_to_variant(item, variant):
if variant.attributes:
variant.description += "\n"
for d in variant.attributes:
variant.description += "<p>" + d.attribute + ": " + cstr(d.attribute_value) + "</p>"
variant.description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"
def make_variant_item_code(template_item_code, template_item_name, variant):
"""Uses template's item code and abbreviations to make variant's item code"""

View File

@ -36,7 +36,7 @@ def execute(filters=None):
status_map = {"Present": "P", "Absent": "A", "Half Day": "HD", "On Leave": "L", "None": "", "Holiday":"<b>H</b>"}
if status == "None" and holiday_map:
emp_holiday_list = emp_det.holiday_list if emp_det.holiday_list else default_holiday_list
if (day+1) in holiday_map[emp_holiday_list]:
if emp_holiday_list in holiday_map and (day+1) in holiday_map[emp_holiday_list]:
status = "Holiday"
row.append(status_map[status])
@ -45,7 +45,7 @@ def execute(filters=None):
elif status == "Absent":
total_a += 1
elif status == "On Leave":
total_l += 1
total_l += 1
elif status == "Half Day":
total_p += 0.5
total_a += 0.5

View File

@ -95,8 +95,8 @@ class BOM(WebsiteGenerator):
self.validate_bom_currecny(item)
ret = self.get_bom_material_detail({
"item_code": item.item_code,
"item_name": item.item_name,
"item_code": item.item_code,
"item_name": item.item_name,
"bom_no": item.bom_no,
"stock_qty": item.stock_qty
})
@ -312,7 +312,7 @@ class BOM(WebsiteGenerator):
li.append("{0} on row {1}".format(i.item_code, i.idx))
duplicate_list = '<br>' + '<br>'.join(li)
frappe.throw(_("Same item has been entered multiple times. {list}").format(list=duplicate_list))
frappe.throw(_("Same item has been entered multiple times. {0}").format(duplicate_list))
def check_recursion(self):
""" Check whether recursion occurs in any bom"""
@ -346,7 +346,7 @@ class BOM(WebsiteGenerator):
count = 0
if not bom_list:
bom_list = []
if self.name not in bom_list:
bom_list.append(self.name)

View File

@ -47,7 +47,7 @@ class Task(Document):
from frappe.desk.form.assign_to import clear
clear(self.doctype, self.name)
def validate_progress(self):
if self.progress > 100:
frappe.throw(_("Progress % for a task cannot be more than 100."))
@ -63,6 +63,12 @@ class Task(Document):
self.check_recursion()
self.reschedule_dependent_tasks()
self.update_project()
self.unassign_todo()
def unassign_todo(self):
if self.status == "Closed" or self.status == "Cancelled":
from frappe.desk.form.assign_to import clear
clear(self.doctype, self.name)
def update_total_expense_claim(self):
self.total_expense_claim = frappe.db.sql("""select sum(total_sanctioned_amount) from `tabExpense Claim`
@ -120,7 +126,7 @@ class Task(Document):
def has_webform_permission(doc):
project_user = frappe.db.get_value("Project User", {"parent": doc.project, "user":frappe.session.user} , "user")
if project_user:
return True
return True
@frappe.whitelist()
def get_events(start, end, filters=None):
@ -154,7 +160,7 @@ def get_project(doctype, txt, searchfield, start, page_len, filters):
order by name
limit %(start)s, %(page_len)s """ % {'key': searchfield,
'txt': "%%%s%%" % frappe.db.escape(txt), 'mcond':get_match_cond(doctype),
'start': start, 'page_len': page_len})
'start': start, 'page_len': page_len})
@frappe.whitelist()
@ -170,4 +176,5 @@ def set_tasks_as_overdue():
where exp_end_date is not null
and exp_end_date < CURDATE()
and `status` not in ('Closed', 'Cancelled')""")

View File

@ -382,7 +382,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
if(me.frm.doc.company && me.frm.fields_dict.currency) {
var company_currency = me.get_company_currency();
var company_doc = frappe.get_doc(":Company", me.frm.doc.company);
if (!me.frm.doc.currency) {
if (!me.frm.doc.currency || me.frm.doc.currency != company_currency) {
me.frm.set_value("currency", company_currency);
}

View File

@ -1473,6 +1473,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "purchase_uom",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Purchase Unit of Measure",
"length": 0,
"no_copy": 0,
"options": "UOM",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -2069,6 +2100,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sales_uom",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Sales Unit of Measure",
"length": 0,
"no_copy": 0,
"options": "UOM",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -3143,7 +3205,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 1,
"modified": "2017-07-06 18:28:36.645217",
"modified": "2017-09-27 14:08:02.948326",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",

View File

@ -164,6 +164,16 @@ def get_basic_details(args, item):
warehouse = user_default_warehouse or item.default_warehouse or args.warehouse
#Set the UOM to the Default Sales UOM or Default Purchase UOM if configured in the Item Master
if args.get('doctype') in ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']:
uom = item.sales_uom if item.sales_uom else item.stock_uom
elif args.get('doctype') in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']:
uom = item.purchase_uom if item.purchase_uom else item.stock_uom
else:
uom = item.stock_uom
args.uom = uom
out = frappe._dict({
"item_code": item.name,
"item_name": item.item_name,
@ -178,7 +188,7 @@ def get_basic_details(args, item):
"batch_no": None,
"item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in
item.get("taxes")))),
"uom": item.stock_uom,
"uom": uom,
"min_order_qty": flt(item.min_order_qty) if args.doctype == "Material Request" else "",
"qty": args.qty or 1.0,
"stock_qty": args.qty or 1.0,

View File

@ -1,11 +1,11 @@
<h2>{{_("Recurring")}} {{ type }} {{ _("Failed")}}</h2>
<p>An error occured while creating recurring {{ type }} <b>{{ name }}</b> for <b>{{ party }}</b>.</p>
<p>This could be because of some invalid Email Addresses in the {{ type }}.</p>
<p>To stop sending repetitive error notifications from the system, we have checked "Disabled" field in the subscription {{ subscription}} for the {{ type }} {{ name }}.</p>
<p><b>Please correct the {{ type }} and unchcked "Disabled" in the {{ subscription }} for making recurring again.</b></p>
<p>{{_("An error occured while creating recurring")}} {{ type }} <b>{{ name }}</b> {{_("for")}} <b>{{ party }}</b>.</p>
<p>{{_("This could be because of some invalid Email Addresses in the")}} {{ type }}.</p>
<p>{{_("To stop sending repetitive error notifications from the system, we have checked "Disabled" field in the subscription")}} {{ subscription}} {{_("for the")}} {{ type }} {{ name }}.</p>
<p><b>{{_("Please correct the")}} {{ type }} {{_('and unchcked "Disabled" in the')}} {{ subscription }} {{_("for making recurring again.")}}</b></p>
<hr>
<p><b>It is necessary to take this action today itself for the above mentioned recurring {{ type }}
to be generated. If delayed, you will have to manually change the "Repeat on Day of Month" field
of this {{ type }} for generating the recurring {{ type }} in the subscription {{ subscription }}.</b></p>
<p>[This email is autogenerated]</p>
<p><b>{{_("It is necessary to take this action today itself for the above mentioned recurring")}} {{ type }}
{{_('to be generated. If delayed, you will have to manually change the "Repeat on Day of Month" field
of this')}} {{ type }} {{_("for generating the recurring")}} {{ type }} {{_("in the subscription")}} {{ subscription }}.</b></p>
<p>[{{_("This email is autogenerated")}}]</p>

View File

@ -128,3 +128,4 @@ erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with
erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js
erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js
erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js
erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_last_purchase_rate.js