Merge branch 'staging'
This commit is contained in:
commit
ff689a658f
@ -4,7 +4,7 @@ import inspect
|
|||||||
import frappe
|
import frappe
|
||||||
from erpnext.hooks import regional_overrides
|
from erpnext.hooks import regional_overrides
|
||||||
|
|
||||||
__version__ = '9.0.9'
|
__version__ = '9.1.0'
|
||||||
|
|
||||||
def get_default_company(user=None):
|
def get_default_company(user=None):
|
||||||
'''Get default company for user'''
|
'''Get default company for user'''
|
||||||
|
@ -286,6 +286,99 @@
|
|||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "currency_exchange_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"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": "Currency Exchange Settings",
|
||||||
|
"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,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "allow_stale",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Allow Stale Exchange Rates",
|
||||||
|
"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,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"default": "1",
|
||||||
|
"depends_on": "eval:doc.allow_stale==0",
|
||||||
|
"fieldname": "stale_days",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"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": "Stale Days",
|
||||||
|
"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
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"has_web_view": 0,
|
||||||
@ -299,7 +392,7 @@
|
|||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2017-06-16 17:39:50.614522",
|
"modified": "2017-09-05 10:10:03.117505",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Accounts Settings",
|
"name": "Accounts Settings",
|
||||||
|
@ -5,10 +5,20 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe.utils import cint
|
||||||
from frappe.utils import cint, comma_and
|
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class AccountsSettings(Document):
|
class AccountsSettings(Document):
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
self.validate_stale_days()
|
||||||
|
|
||||||
|
def validate_stale_days(self):
|
||||||
|
if not self.allow_stale and cint(self.stale_days) <= 0:
|
||||||
|
frappe.msgprint(
|
||||||
|
"Stale Days should start from 1.", title='Error', indicator='red',
|
||||||
|
raise_exception=1)
|
||||||
|
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
QUnit.module('accounts');
|
||||||
|
|
||||||
|
QUnit.test("test: Accounts Settings doesn't allow negatives", function (assert) {
|
||||||
|
let done = assert.async();
|
||||||
|
|
||||||
|
assert.expect(2);
|
||||||
|
|
||||||
|
frappe.run_serially([
|
||||||
|
() => frappe.set_route('Form', 'Accounts Settings', 'Accounts Settings'),
|
||||||
|
() => frappe.timeout(2),
|
||||||
|
() => unchecked_if_checked(cur_frm, 'Allow Stale Exchange Rates', frappe.click_check),
|
||||||
|
() => cur_frm.set_value('stale_days', 0),
|
||||||
|
() => frappe.click_button('Save'),
|
||||||
|
() => frappe.timeout(2),
|
||||||
|
() => {
|
||||||
|
assert.ok(cur_dialog);
|
||||||
|
},
|
||||||
|
() => frappe.click_button('Close'),
|
||||||
|
() => cur_frm.set_value('stale_days', -1),
|
||||||
|
() => frappe.click_button('Save'),
|
||||||
|
() => frappe.timeout(2),
|
||||||
|
() => {
|
||||||
|
assert.ok(cur_dialog);
|
||||||
|
},
|
||||||
|
() => frappe.click_button('Close'),
|
||||||
|
() => done()
|
||||||
|
]);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
const unchecked_if_checked = function(frm, field_name, fn){
|
||||||
|
if (frm.doc.allow_stale) {
|
||||||
|
return fn(field_name);
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,22 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
class TestAccountsSettings(unittest.TestCase):
|
||||||
|
def tearDown(self):
|
||||||
|
# Just in case `save` method succeeds, we need to take things back to default so that other tests
|
||||||
|
# don't break
|
||||||
|
cur_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||||
|
cur_settings.allow_stale = 1
|
||||||
|
cur_settings.save()
|
||||||
|
|
||||||
|
def test_stale_days(self):
|
||||||
|
cur_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||||
|
cur_settings.allow_stale = 0
|
||||||
|
cur_settings.stale_days = 0
|
||||||
|
|
||||||
|
self.assertRaises(frappe.ValidationError, cur_settings.save)
|
||||||
|
|
||||||
|
cur_settings.stale_days = -1
|
||||||
|
self.assertRaises(frappe.ValidationError, cur_settings.save)
|
@ -12,8 +12,8 @@ from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amo
|
|||||||
from erpnext.hr.doctype.employee_loan.employee_loan import update_disbursement_status
|
from erpnext.hr.doctype.employee_loan.employee_loan import update_disbursement_status
|
||||||
|
|
||||||
class JournalEntry(AccountsController):
|
class JournalEntry(AccountsController):
|
||||||
def __init__(self, arg1, arg2=None):
|
def __init__(self, *args, **kwargs):
|
||||||
super(JournalEntry, self).__init__(arg1, arg2)
|
super(JournalEntry, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def get_feed(self):
|
def get_feed(self):
|
||||||
return self.voucher_type
|
return self.voucher_type
|
||||||
|
@ -403,6 +403,13 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
|
|
||||||
frm.events.set_difference_amount(frm);
|
frm.events.set_difference_amount(frm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make read only if Accounts Settings doesn't allow stale rates
|
||||||
|
frappe.model.get_value("Accounts Settings", null, "allow_stale",
|
||||||
|
function(d){
|
||||||
|
frm.set_df_property("source_exchange_rate", "read_only", cint(d.allow_stale) ? 0 : 1);
|
||||||
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
target_exchange_rate: function(frm) {
|
target_exchange_rate: function(frm) {
|
||||||
@ -421,6 +428,13 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.events.set_difference_amount(frm);
|
frm.events.set_difference_amount(frm);
|
||||||
}
|
}
|
||||||
frm.set_paid_amount_based_on_received_amount = false;
|
frm.set_paid_amount_based_on_received_amount = false;
|
||||||
|
|
||||||
|
// Make read only if Accounts Settings doesn't allow stale rates
|
||||||
|
frappe.model.get_value("Accounts Settings", null, "allow_stale",
|
||||||
|
function(d){
|
||||||
|
frm.set_df_property("target_exchange_rate", "read_only", cint(d.allow_stale) ? 0 : 1);
|
||||||
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
paid_amount: function(frm) {
|
paid_amount: function(frm) {
|
||||||
|
@ -22,8 +22,8 @@ form_grid_templates = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PurchaseInvoice(BuyingController):
|
class PurchaseInvoice(BuyingController):
|
||||||
def __init__(self, arg1, arg2=None):
|
def __init__(self, *args, **kwargs):
|
||||||
super(PurchaseInvoice, self).__init__(arg1, arg2)
|
super(PurchaseInvoice, self).__init__(*args, **kwargs)
|
||||||
self.status_updater = [{
|
self.status_updater = [{
|
||||||
'source_dt': 'Purchase Invoice Item',
|
'source_dt': 'Purchase Invoice Item',
|
||||||
'target_dt': 'Purchase Order Item',
|
'target_dt': 'Purchase Order Item',
|
||||||
|
@ -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){
|
project: function(frm){
|
||||||
frm.call({
|
frm.call({
|
||||||
|
@ -27,8 +27,8 @@ form_grid_templates = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SalesInvoice(SellingController):
|
class SalesInvoice(SellingController):
|
||||||
def __init__(self, arg1, arg2=None):
|
def __init__(self, *args, **kwargs):
|
||||||
super(SalesInvoice, self).__init__(arg1, arg2)
|
super(SalesInvoice, self).__init__(*args, **kwargs)
|
||||||
self.status_updater = [{
|
self.status_updater = [{
|
||||||
'source_dt': 'Sales Invoice Item',
|
'source_dt': 'Sales Invoice Item',
|
||||||
'target_field': 'billed_amt',
|
'target_field': 'billed_amt',
|
||||||
|
@ -1084,7 +1084,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
si.items[0].price_list_rate = price_list_rate
|
si.items[0].price_list_rate = price_list_rate
|
||||||
si.items[0].margin_type = 'Percentage'
|
si.items[0].margin_type = 'Percentage'
|
||||||
si.items[0].margin_rate_or_amount = 25
|
si.items[0].margin_rate_or_amount = 25
|
||||||
si.insert()
|
si.save()
|
||||||
self.assertEqual(si.get("items")[0].rate, flt((price_list_rate*25)/100 + price_list_rate))
|
self.assertEqual(si.get("items")[0].rate, flt((price_list_rate*25)/100 + price_list_rate))
|
||||||
|
|
||||||
def test_outstanding_amount_after_advance_jv_cancelation(self):
|
def test_outstanding_amount_after_advance_jv_cancelation(self):
|
||||||
|
@ -498,7 +498,7 @@
|
|||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 1,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"fieldname": "notification",
|
"fieldname": "notification",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
@ -554,6 +554,38 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"depends_on": "eval: doc.notify_by_email",
|
||||||
|
"description": "To add dynamic subject, use jinja tags like\n\n<div><pre><code>New {{ doc.doctype }} #{{ doc.name }}</code></pre></div>",
|
||||||
|
"fieldname": "subject",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"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": "Subject",
|
||||||
|
"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_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -652,6 +684,69 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"depends_on": "eval:doc.notify_by_email",
|
||||||
|
"fieldname": "section_break_20",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"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": "Message",
|
||||||
|
"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,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"default": "Please find attached {{ doc.doctype }} #{{ doc.name }}",
|
||||||
|
"fieldname": "message",
|
||||||
|
"fieldtype": "Text",
|
||||||
|
"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": "Message",
|
||||||
|
"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,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 1,
|
||||||
|
"columns": 0,
|
||||||
|
"depends_on": "eval: !doc.__islocal",
|
||||||
"fieldname": "section_break_16",
|
"fieldname": "section_break_16",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
|
@ -7,6 +7,7 @@ import frappe
|
|||||||
import calendar
|
import calendar
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.desk.form import assign_to
|
from frappe.desk.form import assign_to
|
||||||
|
from frappe.utils.jinja import validate_template
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
from frappe.utils.user import get_system_managers
|
from frappe.utils.user import get_system_managers
|
||||||
from frappe.utils import cstr, getdate, split_emails, add_days, today, get_last_day, get_first_day
|
from frappe.utils import cstr, getdate, split_emails, add_days, today, get_last_day, get_first_day
|
||||||
@ -20,6 +21,9 @@ class Subscription(Document):
|
|||||||
self.validate_next_schedule_date()
|
self.validate_next_schedule_date()
|
||||||
self.validate_email_id()
|
self.validate_email_id()
|
||||||
|
|
||||||
|
validate_template(self.subject or "")
|
||||||
|
validate_template(self.message or "")
|
||||||
|
|
||||||
def before_submit(self):
|
def before_submit(self):
|
||||||
self.set_next_schedule_date()
|
self.set_next_schedule_date()
|
||||||
|
|
||||||
@ -138,19 +142,19 @@ def get_subscription_entries(date):
|
|||||||
def create_documents(data, schedule_date):
|
def create_documents(data, schedule_date):
|
||||||
try:
|
try:
|
||||||
doc = make_new_document(data, schedule_date)
|
doc = make_new_document(data, schedule_date)
|
||||||
if doc.from_date:
|
if getattr(doc, "from_date", None):
|
||||||
update_subscription_period(data, doc)
|
update_subscription_period(data, doc)
|
||||||
|
|
||||||
if data.notify_by_email and data.recipients:
|
if data.notify_by_email and data.recipients:
|
||||||
print_format = data.print_format or "Standard"
|
print_format = data.print_format or "Standard"
|
||||||
send_notification(doc, print_format, data.recipients)
|
send_notification(doc, data, print_format=print_format)
|
||||||
|
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
frappe.db.begin()
|
frappe.db.begin()
|
||||||
frappe.log_error(frappe.get_traceback())
|
frappe.log_error(frappe.get_traceback())
|
||||||
disabled_subscription(data)
|
disable_subscription(data)
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
if data.reference_document and not frappe.flags.in_test:
|
if data.reference_document and not frappe.flags.in_test:
|
||||||
notify_error_to_user(data)
|
notify_error_to_user(data)
|
||||||
@ -162,7 +166,7 @@ def update_subscription_period(data, doc):
|
|||||||
frappe.db.set_value('Subscription', data.name, 'from_date', from_date)
|
frappe.db.set_value('Subscription', data.name, 'from_date', from_date)
|
||||||
frappe.db.set_value('Subscription', data.name, 'to_date', to_date)
|
frappe.db.set_value('Subscription', data.name, 'to_date', to_date)
|
||||||
|
|
||||||
def disabled_subscription(data):
|
def disable_subscription(data):
|
||||||
subscription = frappe.get_doc('Subscription', data.name)
|
subscription = frappe.get_doc('Subscription', data.name)
|
||||||
subscription.db_set('disabled', 1)
|
subscription.db_set('disabled', 1)
|
||||||
|
|
||||||
@ -225,14 +229,25 @@ def get_next_date(dt, mcount, day=None):
|
|||||||
|
|
||||||
return dt
|
return dt
|
||||||
|
|
||||||
def send_notification(new_rv, print_format='Standard', recipients=None):
|
def send_notification(new_rv, subscription_doc, print_format='Standard'):
|
||||||
"""Notify concerned persons about recurring document generation"""
|
"""Notify concerned persons about recurring document generation"""
|
||||||
print_format = print_format
|
print_format = print_format
|
||||||
|
|
||||||
frappe.sendmail(recipients,
|
if not subscription_doc.subject:
|
||||||
subject= _("New {0}: #{1}").format(new_rv.doctype, new_rv.name),
|
subject = _("New {0}: #{1}").format(new_rv.doctype, new_rv.name)
|
||||||
message = _("Please find attached {0} #{1}").format(new_rv.doctype, new_rv.name),
|
elif "{" in subscription_doc.subject:
|
||||||
attachments = [frappe.attach_print(new_rv.doctype, new_rv.name, file_name=new_rv.name, print_format=print_format)])
|
subject = frappe.render_template(subscription_doc.subject, {'doc': new_rv})
|
||||||
|
|
||||||
|
if not subscription_doc.message:
|
||||||
|
message = _("Please find attached {0} #{1}").format(new_rv.doctype, new_rv.name)
|
||||||
|
elif "{" in subscription_doc.message:
|
||||||
|
message = frappe.render_template(subscription_doc.message, {'doc': new_rv})
|
||||||
|
|
||||||
|
attachments = [frappe.attach_print(new_rv.doctype, new_rv.name,
|
||||||
|
file_name=new_rv.name, print_format=print_format)]
|
||||||
|
|
||||||
|
frappe.sendmail(subscription_doc.recipients,
|
||||||
|
subject=subject, message=message, attachments=attachments)
|
||||||
|
|
||||||
def notify_errors(doc, doctype, party, owner, name):
|
def notify_errors(doc, doctype, party, owner, name):
|
||||||
recipients = get_system_managers(only_name=True)
|
recipients = get_system_managers(only_name=True)
|
||||||
|
@ -68,7 +68,8 @@ def set_address_details(out, party, party_type, doctype=None, company=None):
|
|||||||
billing_address_field = "customer_address" if party_type == "Lead" \
|
billing_address_field = "customer_address" if party_type == "Lead" \
|
||||||
else party_type.lower() + "_address"
|
else party_type.lower() + "_address"
|
||||||
out[billing_address_field] = get_default_address(party_type, party.name)
|
out[billing_address_field] = get_default_address(party_type, party.name)
|
||||||
out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field]))
|
if doctype:
|
||||||
|
out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field]))
|
||||||
|
|
||||||
# address display
|
# address display
|
||||||
out.address_display = get_address_display(out[billing_address_field])
|
out.address_display = get_address_display(out[billing_address_field])
|
||||||
@ -77,7 +78,8 @@ def set_address_details(out, party, party_type, doctype=None, company=None):
|
|||||||
if party_type in ["Customer", "Lead"]:
|
if party_type in ["Customer", "Lead"]:
|
||||||
out.shipping_address_name = get_default_address(party_type, party.name, 'is_shipping_address')
|
out.shipping_address_name = get_default_address(party_type, party.name, 'is_shipping_address')
|
||||||
out.shipping_address = get_address_display(out["shipping_address_name"])
|
out.shipping_address = get_address_display(out["shipping_address_name"])
|
||||||
out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name))
|
if doctype:
|
||||||
|
out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name))
|
||||||
|
|
||||||
if doctype and doctype in ['Delivery Note', 'Sales Invoice']:
|
if doctype and doctype in ['Delivery Note', 'Sales Invoice']:
|
||||||
out.update(get_company_address(company))
|
out.update(get_company_address(company))
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
// License: GNU General Public License v3. See license.txt
|
// 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({
|
erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({
|
||||||
refresh: function(doc, cdt, cdn) {
|
refresh: function(doc, cdt, cdn) {
|
||||||
var me = this;
|
var me = this;
|
||||||
@ -214,17 +227,6 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
|||||||
|
|
||||||
delivered_by_supplier: function(){
|
delivered_by_supplier: function(){
|
||||||
cur_frm.cscript.update_status('Deliver', 'Delivered')
|
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();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1206,37 +1206,6 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 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_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -3458,7 +3427,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2017-09-19 11:22:30.190589",
|
"modified": "2017-09-22 16:11:49.856808",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order",
|
"name": "Purchase Order",
|
||||||
|
@ -20,8 +20,8 @@ form_grid_templates = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PurchaseOrder(BuyingController):
|
class PurchaseOrder(BuyingController):
|
||||||
def __init__(self, arg1, arg2=None):
|
def __init__(self, *args, **kwargs):
|
||||||
super(PurchaseOrder, self).__init__(arg1, arg2)
|
super(PurchaseOrder, self).__init__(*args, **kwargs)
|
||||||
self.status_updater = [{
|
self.status_updater = [{
|
||||||
'source_dt': 'Purchase Order Item',
|
'source_dt': 'Purchase Order Item',
|
||||||
'target_dt': 'Material Request Item',
|
'target_dt': 'Material Request Item',
|
||||||
@ -116,14 +116,13 @@ class PurchaseOrder(BuyingController):
|
|||||||
d.discount_percentage = last_purchase_details['discount_percentage']
|
d.discount_percentage = last_purchase_details['discount_percentage']
|
||||||
d.base_rate = last_purchase_details['base_rate'] * (flt(d.conversion_factor) or 1.0)
|
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.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:
|
else:
|
||||||
msgprint(_("Last purchase rate not found"))
|
|
||||||
|
|
||||||
item_last_purchase_rate = frappe.db.get_value("Item", d.item_code, "last_purchase_rate")
|
item_last_purchase_rate = frappe.db.get_value("Item", d.item_code, "last_purchase_rate")
|
||||||
if item_last_purchase_rate:
|
if item_last_purchase_rate:
|
||||||
d.base_price_list_rate = d.base_rate = d.price_list_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
|
# Check for Closed status
|
||||||
def check_for_closed_status(self):
|
def check_for_closed_status(self):
|
||||||
|
@ -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()
|
||||||
|
]);
|
||||||
|
});
|
@ -655,6 +655,37 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 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_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -1714,7 +1745,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2017-08-02 22:15:47.411235",
|
"modified": "2017-09-22 16:47:08.783546",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order Item",
|
"name": "Purchase Order Item",
|
||||||
|
@ -254,6 +254,21 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, __("Get items from"));
|
}, __("Get items from"));
|
||||||
|
// Get items from Opportunity
|
||||||
|
this.frm.add_custom_button(__('Opportunity'),
|
||||||
|
function() {
|
||||||
|
erpnext.utils.map_current_doc({
|
||||||
|
method: "erpnext.crm.doctype.opportunity.opportunity.make_request_for_quotation",
|
||||||
|
source_doctype: "Opportunity",
|
||||||
|
target: me.frm,
|
||||||
|
setters: {
|
||||||
|
company: me.frm.doc.company
|
||||||
|
},
|
||||||
|
get_query_filters: {
|
||||||
|
enquiry_type: "Sales"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, __("Get items from"));
|
||||||
// Get items from open Material Requests based on supplier
|
// Get items from open Material Requests based on supplier
|
||||||
this.frm.add_custom_button(__('Possible Supplier'), function() {
|
this.frm.add_custom_button(__('Possible Supplier'), function() {
|
||||||
// Create a dialog window for the user to pick their supplier
|
// Create a dialog window for the user to pick their supplier
|
||||||
|
@ -268,5 +268,13 @@ def get_data():
|
|||||||
"icon": "octicon octicon-plus",
|
"icon": "octicon octicon-plus",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"label": _("Healthcare")
|
"label": _("Healthcare")
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
"module_name": "Data Import Tool",
|
||||||
|
"color": "#7f8c8d",
|
||||||
|
"icon": "octicon octicon-circuit-board",
|
||||||
|
"type": "page",
|
||||||
|
"link": "data-import-tool",
|
||||||
|
"label": _("Data Import Tool")
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
@ -123,6 +123,12 @@ def get_data():
|
|||||||
"is_query_report": True,
|
"is_query_report": True,
|
||||||
"name": "BOM Search",
|
"name": "BOM Search",
|
||||||
"doctype": "BOM"
|
"doctype": "BOM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "report",
|
||||||
|
"is_query_report": True,
|
||||||
|
"name": "BOM Stock Report",
|
||||||
|
"doctype": "BOM"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -105,6 +105,11 @@ def get_data():
|
|||||||
"name": "Pricing Rule",
|
"name": "Pricing Rule",
|
||||||
"description": _("Rules for applying pricing and discount.")
|
"description": _("Rules for applying pricing and discount.")
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Item Variant Settings",
|
||||||
|
"description": _("Item Variant Settings."),
|
||||||
|
},
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -15,8 +15,8 @@ from erpnext.exceptions import InvalidCurrency
|
|||||||
force_item_fields = ("item_group", "barcode", "brand", "stock_uom")
|
force_item_fields = ("item_group", "barcode", "brand", "stock_uom")
|
||||||
|
|
||||||
class AccountsController(TransactionBase):
|
class AccountsController(TransactionBase):
|
||||||
def __init__(self, arg1, arg2=None):
|
def __init__(self, *args, **kwargs):
|
||||||
super(AccountsController, self).__init__(arg1, arg2)
|
super(AccountsController, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def company_currency(self):
|
def company_currency(self):
|
||||||
@ -187,9 +187,6 @@ class AccountsController(TransactionBase):
|
|||||||
if stock_qty != len(get_serial_nos(item.get('serial_no'))):
|
if stock_qty != len(get_serial_nos(item.get('serial_no'))):
|
||||||
item.set(fieldname, value)
|
item.set(fieldname, value)
|
||||||
|
|
||||||
elif fieldname == "conversion_factor" and not item.get("conversion_factor"):
|
|
||||||
item.set(fieldname, value)
|
|
||||||
|
|
||||||
if ret.get("pricing_rule"):
|
if ret.get("pricing_rule"):
|
||||||
# if user changed the discount percentage then set user's discount percentage ?
|
# if user changed the discount percentage then set user's discount percentage ?
|
||||||
item.set("discount_percentage", ret.get("discount_percentage"))
|
item.set("discount_percentage", ret.get("discount_percentage"))
|
||||||
|
@ -61,7 +61,7 @@ class BuyingController(StockController):
|
|||||||
|
|
||||||
# set contact and address details for supplier, if they are not mentioned
|
# set contact and address details for supplier, if they are not mentioned
|
||||||
if getattr(self, "supplier", None):
|
if getattr(self, "supplier", None):
|
||||||
self.update_if_missing(get_party_details(self.supplier, party_type="Supplier", ignore_permissions=self.flags.ignore_permissions))
|
self.update_if_missing(get_party_details(self.supplier, party_type="Supplier", ignore_permissions=self.flags.ignore_permissions, doctype=self.doctype, company=self.company))
|
||||||
|
|
||||||
self.set_missing_item_details(for_validate)
|
self.set_missing_item_details(for_validate)
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import cstr, flt
|
from frappe.utils import cstr, flt
|
||||||
import json
|
import json, copy
|
||||||
|
|
||||||
class ItemVariantExistsError(frappe.ValidationError): pass
|
class ItemVariantExistsError(frappe.ValidationError): pass
|
||||||
class InvalidItemAttributeValueError(frappe.ValidationError): pass
|
class InvalidItemAttributeValueError(frappe.ValidationError): pass
|
||||||
@ -174,18 +174,30 @@ def copy_attributes_to_variant(item, variant):
|
|||||||
|
|
||||||
# copy non no-copy fields
|
# copy non no-copy fields
|
||||||
|
|
||||||
exclude_fields = ["item_code", "item_name", "show_in_website"]
|
exclude_fields = ["naming_series", "item_code", "item_name", "show_in_website",
|
||||||
|
"show_variant_in_website", "opening_stock", "variant_of", "valuation_rate"]
|
||||||
|
|
||||||
if item.variant_based_on=='Manufacturer':
|
if item.variant_based_on=='Manufacturer':
|
||||||
# don't copy manufacturer values if based on part no
|
# don't copy manufacturer values if based on part no
|
||||||
exclude_fields += ['manufacturer', 'manufacturer_part_no']
|
exclude_fields += ['manufacturer', 'manufacturer_part_no']
|
||||||
|
|
||||||
|
allow_fields = [d.field_name for d in frappe.get_all("Variant Field", fields = ['field_name'])]
|
||||||
|
if "variant_based_on" not in allow_fields:
|
||||||
|
allow_fields.append("variant_based_on")
|
||||||
for field in item.meta.fields:
|
for field in item.meta.fields:
|
||||||
# "Table" is part of `no_value_field` but we shouldn't ignore tables
|
# "Table" is part of `no_value_field` but we shouldn't ignore tables
|
||||||
if (field.fieldtype == 'Table' or field.fieldtype not in no_value_fields) \
|
if (field.reqd or field.fieldname in allow_fields) and field.fieldname not in exclude_fields:
|
||||||
and (not field.no_copy) and field.fieldname not in exclude_fields:
|
|
||||||
if variant.get(field.fieldname) != item.get(field.fieldname):
|
if variant.get(field.fieldname) != item.get(field.fieldname):
|
||||||
variant.set(field.fieldname, item.get(field.fieldname))
|
if field.fieldtype == "Table":
|
||||||
|
variant.set(field.fieldname, [])
|
||||||
|
for d in item.get(field.fieldname):
|
||||||
|
row = copy.deepcopy(d)
|
||||||
|
if row.get("name"):
|
||||||
|
row.name = None
|
||||||
|
variant.append(field.fieldname, row)
|
||||||
|
else:
|
||||||
|
variant.set(field.fieldname, item.get(field.fieldname))
|
||||||
|
|
||||||
variant.variant_of = item.name
|
variant.variant_of = item.name
|
||||||
variant.has_variants = 0
|
variant.has_variants = 0
|
||||||
if not variant.description:
|
if not variant.description:
|
||||||
|
@ -49,7 +49,8 @@ class SellingController(StockController):
|
|||||||
if getattr(self, "customer", None):
|
if getattr(self, "customer", None):
|
||||||
from erpnext.accounts.party import _get_party_details
|
from erpnext.accounts.party import _get_party_details
|
||||||
party_details = _get_party_details(self.customer,
|
party_details = _get_party_details(self.customer,
|
||||||
ignore_permissions=self.flags.ignore_permissions)
|
ignore_permissions=self.flags.ignore_permissions,
|
||||||
|
doctype=self.doctype, company=self.company)
|
||||||
if not self.meta.get_field("sales_team"):
|
if not self.meta.get_field("sales_team"):
|
||||||
party_details.pop("sales_team")
|
party_details.pop("sales_team")
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import frappe
|
|||||||
import json
|
import json
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
from erpnext.stock.doctype.item.test_item import set_item_variant_settings
|
||||||
from erpnext.controllers.item_variant import copy_attributes_to_variant, make_variant_item_code
|
from erpnext.controllers.item_variant import copy_attributes_to_variant, make_variant_item_code
|
||||||
|
|
||||||
# python 3 compatibility stuff
|
# python 3 compatibility stuff
|
||||||
@ -54,5 +55,7 @@ def make_item_variant():
|
|||||||
|
|
||||||
class TestItemVariant(unittest.TestCase):
|
class TestItemVariant(unittest.TestCase):
|
||||||
def test_tables_in_template_copied_to_variant(self):
|
def test_tables_in_template_copied_to_variant(self):
|
||||||
|
fields = [{'field_name': 'quality_parameters'}]
|
||||||
|
set_item_variant_settings(fields)
|
||||||
variant = make_item_variant()
|
variant = make_item_variant()
|
||||||
self.assertNotEqual(variant.get("quality_parameters"), [])
|
self.assertNotEqual(variant.get("quality_parameters"), [])
|
||||||
|
@ -42,10 +42,28 @@ class Opportunity(TransactionBase):
|
|||||||
if not self.with_items:
|
if not self.with_items:
|
||||||
self.items = []
|
self.items = []
|
||||||
|
|
||||||
|
|
||||||
def make_new_lead_if_required(self):
|
def make_new_lead_if_required(self):
|
||||||
"""Set lead against new opportunity"""
|
"""Set lead against new opportunity"""
|
||||||
if not (self.lead or self.customer) and self.contact_email:
|
if not (self.lead or self.customer) and self.contact_email:
|
||||||
|
# check if customer is already created agains the self.contact_email
|
||||||
|
customer = frappe.db.sql("""select
|
||||||
|
distinct `tabDynamic Link`.link_name as customer
|
||||||
|
from
|
||||||
|
`tabContact`,
|
||||||
|
`tabDynamic Link`
|
||||||
|
where `tabContact`.email_id='{0}'
|
||||||
|
and
|
||||||
|
`tabContact`.name=`tabDynamic Link`.parent
|
||||||
|
and
|
||||||
|
ifnull(`tabDynamic Link`.link_name, '')<>''
|
||||||
|
and
|
||||||
|
`tabDynamic Link`.link_doctype='Customer'
|
||||||
|
""".format(self.contact_email), as_dict=True)
|
||||||
|
if customer and customer[0].customer:
|
||||||
|
self.customer = customer[0].customer
|
||||||
|
self.enquiry_from = "Customer"
|
||||||
|
return
|
||||||
|
|
||||||
lead_name = frappe.db.get_value("Lead", {"email_id": self.contact_email})
|
lead_name = frappe.db.get_value("Lead", {"email_id": self.contact_email})
|
||||||
if not lead_name:
|
if not lead_name:
|
||||||
sender_name = get_fullname(self.contact_email)
|
sender_name = get_fullname(self.contact_email)
|
||||||
@ -245,6 +263,27 @@ def make_quotation(source_name, target_doc=None):
|
|||||||
|
|
||||||
return doclist
|
return doclist
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def make_request_for_quotation(source_name, target_doc=None):
|
||||||
|
doclist = get_mapped_doc("Opportunity", source_name, {
|
||||||
|
"Opportunity": {
|
||||||
|
"doctype": "Request for Quotation",
|
||||||
|
"validation": {
|
||||||
|
"enquiry_type": ["=", "Sales"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Opportunity Item": {
|
||||||
|
"doctype": "Request for Quotation Item",
|
||||||
|
"field_map": [
|
||||||
|
["name", "opportunity_item"],
|
||||||
|
["parent", "opportunity"],
|
||||||
|
["uom", "uom"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}, target_doc)
|
||||||
|
|
||||||
|
return doclist
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_supplier_quotation(source_name, target_doc=None):
|
def make_supplier_quotation(source_name, target_doc=None):
|
||||||
doclist = get_mapped_doc("Opportunity", source_name, {
|
doclist = get_mapped_doc("Opportunity", source_name, {
|
||||||
@ -284,4 +323,4 @@ def auto_close_opportunity():
|
|||||||
doc.status = "Closed"
|
doc.status = "Closed"
|
||||||
doc.flags.ignore_permissions = True
|
doc.flags.ignore_permissions = True
|
||||||
doc.flags.ignore_mandatory = True
|
doc.flags.ignore_mandatory = True
|
||||||
doc.save()
|
doc.save()
|
||||||
|
@ -4,6 +4,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import today
|
from frappe.utils import today
|
||||||
|
from erpnext.crm.doctype.lead.lead import make_customer
|
||||||
from erpnext.crm.doctype.opportunity.opportunity import make_quotation
|
from erpnext.crm.doctype.opportunity.opportunity import make_quotation
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
@ -25,12 +26,45 @@ class TestOpportunity(unittest.TestCase):
|
|||||||
doc = frappe.get_doc('Opportunity', doc.name)
|
doc = frappe.get_doc('Opportunity', doc.name)
|
||||||
self.assertEquals(doc.status, "Quotation")
|
self.assertEquals(doc.status, "Quotation")
|
||||||
|
|
||||||
|
def test_make_new_lead_if_required(self):
|
||||||
|
args = {
|
||||||
|
"doctype": "Opportunity",
|
||||||
|
"contact_email":"new.opportunity@example.com",
|
||||||
|
"enquiry_type": "Sales",
|
||||||
|
"with_items": 0,
|
||||||
|
"transaction_date": today()
|
||||||
|
}
|
||||||
|
# new lead should be created against the new.opportunity@example.com
|
||||||
|
opp_doc = frappe.get_doc(args).insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
self.assertTrue(opp_doc.lead)
|
||||||
|
self.assertEquals(opp_doc.enquiry_from, "Lead")
|
||||||
|
self.assertEquals(frappe.db.get_value("Lead", opp_doc.lead, "email_id"),
|
||||||
|
'new.opportunity@example.com')
|
||||||
|
|
||||||
|
# create new customer and create new contact against 'new.opportunity@example.com'
|
||||||
|
customer = make_customer(opp_doc.lead).insert(ignore_permissions=True)
|
||||||
|
frappe.get_doc({
|
||||||
|
"doctype": "Contact",
|
||||||
|
"email_id": "new.opportunity@example.com",
|
||||||
|
"first_name": "_Test Opportunity Customer",
|
||||||
|
"links": [{
|
||||||
|
"link_doctype": "Customer",
|
||||||
|
"link_name": customer.name
|
||||||
|
}]
|
||||||
|
}).insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
opp_doc = frappe.get_doc(args).insert(ignore_permissions=True)
|
||||||
|
self.assertTrue(opp_doc.customer)
|
||||||
|
self.assertEquals(opp_doc.enquiry_from, "Customer")
|
||||||
|
self.assertEquals(opp_doc.customer, customer.name)
|
||||||
|
|
||||||
def make_opportunity(**args):
|
def make_opportunity(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|
||||||
opp_doc = frappe.get_doc({
|
opp_doc = frappe.get_doc({
|
||||||
"doctype": "Opportunity",
|
"doctype": "Opportunity",
|
||||||
"enquiry_from": "Customer" or args.enquiry_from,
|
"enquiry_from": args.enquiry_from or "Customer",
|
||||||
"enquiry_type": "Sales",
|
"enquiry_type": "Sales",
|
||||||
"with_items": args.with_items or 0,
|
"with_items": args.with_items or 0,
|
||||||
"transaction_date": today()
|
"transaction_date": today()
|
||||||
|
@ -278,5 +278,16 @@
|
|||||||
"item_code": "Autocad",
|
"item_code": "Autocad",
|
||||||
"item_name": "Autocad",
|
"item_name": "Autocad",
|
||||||
"item_group": "All Item Groups"
|
"item_group": "All Item Groups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"has_batch_no": 1,
|
||||||
|
"create_new_batch": 1,
|
||||||
|
"valuation_rate": 200,
|
||||||
|
"default_warehouse": "Stores",
|
||||||
|
"description": "Corrugated Box",
|
||||||
|
"item_code": "Corrugated Box",
|
||||||
|
"item_name": "Corrugated Box",
|
||||||
|
"item_group": "All Item Groups"
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -16,6 +16,7 @@ def setup(domain):
|
|||||||
setup_user()
|
setup_user()
|
||||||
setup_employee()
|
setup_employee()
|
||||||
setup_user_roles()
|
setup_user_roles()
|
||||||
|
setup_role_permissions()
|
||||||
|
|
||||||
employees = frappe.get_all('Employee', fields=['name', 'date_of_joining'])
|
employees = frappe.get_all('Employee', fields=['name', 'date_of_joining'])
|
||||||
|
|
||||||
@ -91,7 +92,8 @@ def setup_fiscal_year():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
# set the last fiscal year (current year) as default
|
# set the last fiscal year (current year) as default
|
||||||
fiscal_year.set_as_default()
|
if fiscal_year:
|
||||||
|
fiscal_year.set_as_default()
|
||||||
|
|
||||||
def setup_holiday_list():
|
def setup_holiday_list():
|
||||||
"""Setup Holiday List for the current year"""
|
"""Setup Holiday List for the current year"""
|
||||||
@ -374,6 +376,22 @@ def setup_pos_profile():
|
|||||||
|
|
||||||
pos.insert()
|
pos.insert()
|
||||||
|
|
||||||
|
def setup_role_permissions():
|
||||||
|
role_permissions = {'Batch': ['Accounts User', 'Item Manager']}
|
||||||
|
for doctype, roles in role_permissions.items():
|
||||||
|
for role in roles:
|
||||||
|
if not frappe.db.get_value('Custom DocPerm',
|
||||||
|
{'parent': doctype, 'role': role}):
|
||||||
|
frappe.get_doc({
|
||||||
|
'doctype': 'Custom DocPerm',
|
||||||
|
'role': role,
|
||||||
|
'read': 1,
|
||||||
|
'write': 1,
|
||||||
|
'create': 1,
|
||||||
|
'delete': 1,
|
||||||
|
'parent': doctype
|
||||||
|
}).insert(ignore_permissions=True)
|
||||||
|
|
||||||
def import_json(doctype, submit=False, values=None):
|
def import_json(doctype, submit=False, values=None):
|
||||||
frappe.flags.in_import = True
|
frappe.flags.in_import = True
|
||||||
data = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data',
|
data = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data',
|
||||||
|
@ -7,6 +7,7 @@ import frappe, random
|
|||||||
from frappe.desk import query_report
|
from frappe.desk import query_report
|
||||||
from erpnext.stock.stock_ledger import NegativeStockError
|
from erpnext.stock.stock_ledger import NegativeStockError
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import SerialNoRequiredError, SerialNoQtyError
|
from erpnext.stock.doctype.serial_no.serial_no import SerialNoRequiredError, SerialNoQtyError
|
||||||
|
from erpnext.stock.doctype.batch.batch import UnableToSelectBatchError
|
||||||
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return
|
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return
|
||||||
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_return
|
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_return
|
||||||
|
|
||||||
@ -59,7 +60,7 @@ def make_delivery_note():
|
|||||||
try:
|
try:
|
||||||
dn.submit()
|
dn.submit()
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
except (NegativeStockError, SerialNoRequiredError, SerialNoQtyError):
|
except (NegativeStockError, SerialNoRequiredError, SerialNoQtyError, UnableToSelectBatchError):
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
|
|
||||||
def make_stock_reconciliation():
|
def make_stock_reconciliation():
|
||||||
|
BIN
erpnext/docs/assets/img/stock/item_variants_settings.png
Normal file
BIN
erpnext/docs/assets/img/stock/item_variants_settings.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
@ -13,4 +13,8 @@
|
|||||||
|
|
||||||
* Unlink Payment on Cancellation of Invoice: If checked, system will unlink the payment against the invoice. Otherwise, it will show the link error.
|
* Unlink Payment on Cancellation of Invoice: If checked, system will unlink the payment against the invoice. Otherwise, it will show the link error.
|
||||||
|
|
||||||
|
* Allow Stale Exchange Rate: This should be unchecked if you want ERPNext to check the age of records fetched from Currency Exchange in foreign currency transactions. If it is unchecked, the exchange rate field will be read-only in documents.
|
||||||
|
|
||||||
|
* Stale Days: The number of days to use when deciding if a Currency Exchange record is stale. E.g If Currency Exchange records are to be updated every day, the Stale Days should be set as 1.
|
||||||
|
|
||||||
{next}
|
{next}
|
||||||
|
@ -48,4 +48,11 @@ When you make a new Variant, the system will prompt you to select a Manufacturer
|
|||||||
<img class='screenshot' alt='Setup Item Variant by Manufacturer'
|
<img class='screenshot' alt='Setup Item Variant by Manufacturer'
|
||||||
src='/docs/assets/img/stock/set-variant-by-mfg.png'>
|
src='/docs/assets/img/stock/set-variant-by-mfg.png'>
|
||||||
|
|
||||||
The naming of the variant will be the name (ID) of the template Item with a number suffix. e.g. "ITEM000" will have variant "ITEM000-1"
|
The naming of the variant will be the name (ID) of the template Item with a number suffix. e.g. "ITEM000" will have variant "ITEM000-1"
|
||||||
|
|
||||||
|
### Update Variants Based on Template
|
||||||
|
To update the value in the variants items from the template item, select the respective fields first in the Item Variant Settings page. After that system will update the value of that fields in the variants if that values has been changed in the template item.
|
||||||
|
|
||||||
|
To set the fields Goto Stock > Item Variant Settings
|
||||||
|
<img class='screenshot' alt='Item Variant Settings'
|
||||||
|
src='/docs/assets/img/stock/item_variants_settings.png'>
|
||||||
|
@ -11,7 +11,7 @@ app_email = "info@erpnext.com"
|
|||||||
app_license = "GNU General Public License (v3)"
|
app_license = "GNU General Public License (v3)"
|
||||||
source_link = "https://github.com/frappe/erpnext"
|
source_link = "https://github.com/frappe/erpnext"
|
||||||
|
|
||||||
develop_version = '8.x.x-beta'
|
develop_version = '9.x.x-develop'
|
||||||
|
|
||||||
error_report_email = "support@erpnext.com"
|
error_report_email = "support@erpnext.com"
|
||||||
|
|
||||||
@ -27,7 +27,6 @@ doctype_js = {
|
|||||||
# setup wizard
|
# setup wizard
|
||||||
setup_wizard_requires = "assets/erpnext/js/setup_wizard.js"
|
setup_wizard_requires = "assets/erpnext/js/setup_wizard.js"
|
||||||
setup_wizard_complete = "erpnext.setup.setup_wizard.setup_wizard.setup_complete"
|
setup_wizard_complete = "erpnext.setup.setup_wizard.setup_wizard.setup_complete"
|
||||||
setup_wizard_success = "erpnext.setup.setup_wizard.setup_wizard.setup_success"
|
|
||||||
setup_wizard_test = "erpnext.setup.setup_wizard.test_setup_wizard.run_setup_wizard_test"
|
setup_wizard_test = "erpnext.setup.setup_wizard.test_setup_wizard.run_setup_wizard_test"
|
||||||
|
|
||||||
before_install = "erpnext.setup.install.check_setup_wizard_not_completed"
|
before_install = "erpnext.setup.install.check_setup_wizard_not_completed"
|
||||||
@ -48,7 +47,7 @@ treeviews = ['Account', 'Cost Center', 'Warehouse', 'Item Group', 'Customer Grou
|
|||||||
update_website_context = "erpnext.shopping_cart.utils.update_website_context"
|
update_website_context = "erpnext.shopping_cart.utils.update_website_context"
|
||||||
my_account_context = "erpnext.shopping_cart.utils.update_my_account_context"
|
my_account_context = "erpnext.shopping_cart.utils.update_my_account_context"
|
||||||
|
|
||||||
email_append_to = ["Job Applicant", "Opportunity", "Issue"]
|
email_append_to = ["Job Applicant", "Lead", "Opportunity", "Issue"]
|
||||||
|
|
||||||
calendars = ["Task", "Production Order", "Leave Application", "Sales Order", "Holiday List"]
|
calendars = ["Task", "Production Order", "Leave Application", "Sales Order", "Holiday List"]
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@
|
|||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 1,
|
||||||
"label": "Full Name",
|
"label": "Full Name",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
@ -431,7 +431,7 @@
|
|||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "gender",
|
"oldfieldname": "gender",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "Gender",
|
"options": "Gender",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"print_hide_if_no_value": 0,
|
"print_hide_if_no_value": 0,
|
||||||
@ -2432,7 +2432,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2017-06-13 14:29:13.694009",
|
"modified": "2017-10-04 11:42:02.495731",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Employee",
|
"name": "Employee",
|
||||||
|
@ -30,12 +30,13 @@ class BOMUpdateTool(Document):
|
|||||||
frappe.throw(_("The selected BOMs are not for the same item"))
|
frappe.throw(_("The selected BOMs are not for the same item"))
|
||||||
|
|
||||||
def update_new_bom(self):
|
def update_new_bom(self):
|
||||||
current_bom_unitcost = frappe.db.sql("""select total_cost/quantity
|
new_bom_unitcost = frappe.db.sql("""select total_cost/quantity
|
||||||
from `tabBOM` where name = %s""", self.current_bom)
|
from `tabBOM` where name = %s""", self.new_bom)
|
||||||
current_bom_unitcost = current_bom_unitcost and flt(current_bom_unitcost[0][0]) or 0
|
new_bom_unitcost = flt(new_bom_unitcost[0][0]) if new_bom_unitcost else 0
|
||||||
|
|
||||||
frappe.db.sql("""update `tabBOM Item` set bom_no=%s,
|
frappe.db.sql("""update `tabBOM Item` set bom_no=%s,
|
||||||
rate=%s, amount=stock_qty*%s where bom_no = %s and docstatus < 2""",
|
rate=%s, amount=stock_qty*%s where bom_no = %s and docstatus < 2""",
|
||||||
(self.new_bom, current_bom_unitcost, current_bom_unitcost, self.current_bom))
|
(self.new_bom, new_bom_unitcost, new_bom_unitcost, self.current_bom))
|
||||||
|
|
||||||
def get_parent_boms(self):
|
def get_parent_boms(self):
|
||||||
return [d[0] for d in frappe.db.sql("""select distinct parent
|
return [d[0] for d in frappe.db.sql("""select distinct parent
|
||||||
|
@ -59,8 +59,6 @@ QUnit.test("test: production order", function (assert) {
|
|||||||
// Confirm the production order timesheet, save and submit it
|
// Confirm the production order timesheet, save and submit it
|
||||||
() => frappe.click_link("TS-00"),
|
() => frappe.click_link("TS-00"),
|
||||||
() => frappe.timeout(1),
|
() => frappe.timeout(1),
|
||||||
() => frappe.click_button("Save"),
|
|
||||||
() => frappe.timeout(1),
|
|
||||||
() => frappe.click_button("Submit"),
|
() => frappe.click_button("Submit"),
|
||||||
() => frappe.timeout(1),
|
() => frappe.timeout(1),
|
||||||
() => frappe.click_button("Yes"),
|
() => frappe.click_button("Yes"),
|
||||||
|
@ -12,10 +12,6 @@ from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
|
|||||||
from erpnext.manufacturing.doctype.production_order.production_order import get_item_details
|
from erpnext.manufacturing.doctype.production_order.production_order import get_item_details
|
||||||
|
|
||||||
class ProductionPlanningTool(Document):
|
class ProductionPlanningTool(Document):
|
||||||
def __init__(self, arg1, arg2=None):
|
|
||||||
super(ProductionPlanningTool, self).__init__(arg1, arg2)
|
|
||||||
self.item_dict = {}
|
|
||||||
|
|
||||||
def clear_table(self, table_name):
|
def clear_table(self, table_name):
|
||||||
self.set(table_name, [])
|
self.set(table_name, [])
|
||||||
|
|
||||||
@ -398,6 +394,9 @@ class ProductionPlanningTool(Document):
|
|||||||
return bom_wise_item_details
|
return bom_wise_item_details
|
||||||
|
|
||||||
def make_items_dict(self, item_list):
|
def make_items_dict(self, item_list):
|
||||||
|
if not getattr(self, "item_dict", None):
|
||||||
|
self.item_dict = {}
|
||||||
|
|
||||||
for i in item_list:
|
for i in item_list:
|
||||||
self.item_dict.setdefault(i[0], []).append([flt(i[1]), i[2], i[3], i[4], i[5]])
|
self.item_dict.setdefault(i[0], []).append([flt(i[1]), i[2], i[3], i[4], i[5]])
|
||||||
|
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
<h1 class="text-left"><b>{%= __("BOM Stock Report") %}</b></h1>
|
||||||
|
<h5 class="text-left">{%= filters.bom %}</h5>
|
||||||
|
<h5 class="text-left">{%= filters.warehouse %}</h5>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 15%">{%= __("Item") %}</th>
|
||||||
|
<th style="width: 35%">{%= __("Description") %}</th>
|
||||||
|
<th style="width: 14%">{%= __("Required Qty") %}</th>
|
||||||
|
<th style="width: 13%">{%= __("In Stock Qty") %}</th>
|
||||||
|
<th style="width: 23%">{%= __("Enough Parts to Build") %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for(var i=0, l=data.length; i<l; i++) { %}
|
||||||
|
<tr>
|
||||||
|
<td>{%= data[i][ __("Item")] %}</td>
|
||||||
|
<td>{%= data[i][ __("Description")] %} </td>
|
||||||
|
<td align="right">{%= data[i][ __("Required Qty")] %} </td>
|
||||||
|
<td align="right">{%= data[i][ __("In Stock Qty")] %} </td>
|
||||||
|
<td align="right">{%= data[i][ __("Enough Parts to Build")] %} </td>
|
||||||
|
</tr>
|
||||||
|
{% } %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
@ -441,9 +441,11 @@ erpnext.patches.v8_7.set_offline_in_pos_settings #11-09-17
|
|||||||
erpnext.patches.v8_9.add_setup_progress_actions #08-09-2017 #26-09-2017
|
erpnext.patches.v8_9.add_setup_progress_actions #08-09-2017 #26-09-2017
|
||||||
erpnext.patches.v8_9.rename_company_sales_target_field
|
erpnext.patches.v8_9.rename_company_sales_target_field
|
||||||
erpnext.patches.v8_8.set_bom_rate_as_per_uom
|
erpnext.patches.v8_8.set_bom_rate_as_per_uom
|
||||||
|
erpnext.patches.v8_8.add_new_fields_in_accounts_settings
|
||||||
erpnext.patches.v8_9.set_print_zero_amount_taxes
|
erpnext.patches.v8_9.set_print_zero_amount_taxes
|
||||||
erpnext.patches.v8_9.set_default_customer_group
|
erpnext.patches.v8_9.set_default_customer_group
|
||||||
erpnext.patches.v8_9.remove_employee_from_salary_structure_parent
|
erpnext.patches.v8_9.remove_employee_from_salary_structure_parent
|
||||||
erpnext.patches.v8_9.delete_gst_doctypes_for_outside_india_accounts
|
erpnext.patches.v8_9.delete_gst_doctypes_for_outside_india_accounts
|
||||||
|
erpnext.patches.v8_9.set_default_fields_in_variant_settings
|
||||||
erpnext.patches.v8_9.update_billing_gstin_for_indian_account
|
erpnext.patches.v8_9.update_billing_gstin_for_indian_account
|
||||||
erpnext.patches.v9_0.fix_subscription_next_date
|
erpnext.patches.v9_0.fix_subscription_next_date
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.db.sql(
|
||||||
|
"INSERT INTO `tabSingles` (`doctype`, `field`, `value`) VALUES ('Accounts Settings', 'allow_stale', '1'), "
|
||||||
|
"('Accounts Settings', 'stale_days', '1')"
|
||||||
|
)
|
@ -0,0 +1,13 @@
|
|||||||
|
# Copyright (c) 2017, Frappe and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'item_variant_settings')
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'variant_field')
|
||||||
|
|
||||||
|
doc = frappe.get_doc('Item Variant Settings')
|
||||||
|
doc.set_default_fields()
|
||||||
|
doc.save()
|
@ -53,12 +53,17 @@ class Project(Document):
|
|||||||
return frappe.get_all("Task", "*", {"project": self.name}, order_by="exp_start_date asc")
|
return frappe.get_all("Task", "*", {"project": self.name}, order_by="exp_start_date asc")
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self.validate_project_name()
|
||||||
self.validate_dates()
|
self.validate_dates()
|
||||||
self.validate_weights()
|
self.validate_weights()
|
||||||
self.sync_tasks()
|
self.sync_tasks()
|
||||||
self.tasks = []
|
self.tasks = []
|
||||||
self.send_welcome_email()
|
self.send_welcome_email()
|
||||||
|
|
||||||
|
def validate_project_name(self):
|
||||||
|
if self.get("__islocal") and frappe.db.exists("Project", self.project_name):
|
||||||
|
frappe.throw(_("Project {0} already exists").format(self.project_name))
|
||||||
|
|
||||||
def validate_dates(self):
|
def validate_dates(self):
|
||||||
if self.expected_start_date and self.expected_end_date:
|
if self.expected_start_date and self.expected_end_date:
|
||||||
if getdate(self.expected_end_date) < getdate(self.expected_start_date):
|
if getdate(self.expected_end_date) < getdate(self.expected_start_date):
|
||||||
|
@ -47,7 +47,7 @@ class Task(Document):
|
|||||||
|
|
||||||
from frappe.desk.form.assign_to import clear
|
from frappe.desk.form.assign_to import clear
|
||||||
clear(self.doctype, self.name)
|
clear(self.doctype, self.name)
|
||||||
|
|
||||||
def validate_progress(self):
|
def validate_progress(self):
|
||||||
if self.progress > 100:
|
if self.progress > 100:
|
||||||
frappe.throw(_("Progress % for a task cannot be more than 100."))
|
frappe.throw(_("Progress % for a task cannot be more than 100."))
|
||||||
@ -63,6 +63,12 @@ class Task(Document):
|
|||||||
self.check_recursion()
|
self.check_recursion()
|
||||||
self.reschedule_dependent_tasks()
|
self.reschedule_dependent_tasks()
|
||||||
self.update_project()
|
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):
|
def update_total_expense_claim(self):
|
||||||
self.total_expense_claim = frappe.db.sql("""select sum(total_sanctioned_amount) from `tabExpense Claim`
|
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):
|
def has_webform_permission(doc):
|
||||||
project_user = frappe.db.get_value("Project User", {"parent": doc.project, "user":frappe.session.user} , "user")
|
project_user = frappe.db.get_value("Project User", {"parent": doc.project, "user":frappe.session.user} , "user")
|
||||||
if project_user:
|
if project_user:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_events(start, end, filters=None):
|
def get_events(start, end, filters=None):
|
||||||
@ -154,7 +160,7 @@ def get_project(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
order by name
|
order by name
|
||||||
limit %(start)s, %(page_len)s """ % {'key': searchfield,
|
limit %(start)s, %(page_len)s """ % {'key': searchfield,
|
||||||
'txt': "%%%s%%" % frappe.db.escape(txt), 'mcond':get_match_cond(doctype),
|
'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()
|
@frappe.whitelist()
|
||||||
@ -170,4 +176,5 @@ def set_tasks_as_overdue():
|
|||||||
where exp_end_date is not null
|
where exp_end_date is not null
|
||||||
and exp_end_date < CURDATE()
|
and exp_end_date < CURDATE()
|
||||||
and `status` not in ('Closed', 'Cancelled')""")
|
and `status` not in ('Closed', 'Cancelled')""")
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,27 +101,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
return me.set_query_for_batch(doc, cdt, cdn)
|
return me.set_query_for_batch(doc, cdt, cdn)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
|
||||||
onload: function() {
|
|
||||||
var me = this;
|
|
||||||
if(this.frm.doc.__islocal) {
|
|
||||||
var today = frappe.datetime.get_today(),
|
|
||||||
currency = frappe.defaults.get_user_default("currency");
|
|
||||||
|
|
||||||
$.each({
|
|
||||||
currency: currency,
|
|
||||||
price_list_currency: currency,
|
|
||||||
status: "Draft",
|
|
||||||
is_subcontracted: "No",
|
|
||||||
}, function(fieldname, value) {
|
|
||||||
if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname])
|
|
||||||
me.frm.set_value(fieldname, value);
|
|
||||||
});
|
|
||||||
|
|
||||||
if(this.frm.doc.company && !this.frm.doc.amended_from) {
|
|
||||||
this.frm.trigger("company");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.frm.fields_dict["taxes"]) {
|
if(this.frm.fields_dict["taxes"]) {
|
||||||
this["taxes_remove"] = this.calculate_taxes_and_totals;
|
this["taxes_remove"] = this.calculate_taxes_and_totals;
|
||||||
@ -153,11 +132,36 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
filters: filters
|
filters: filters
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
onload: function() {
|
||||||
|
var me = this;
|
||||||
|
|
||||||
this.setup_quality_inspection();
|
this.setup_quality_inspection();
|
||||||
|
|
||||||
|
if(this.frm.doc.__islocal) {
|
||||||
|
var currency = frappe.defaults.get_user_default("currency");
|
||||||
|
|
||||||
|
let set_value = (fieldname, value) => {
|
||||||
|
if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname]) {
|
||||||
|
return me.frm.set_value(fieldname, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return frappe.run_serially([
|
||||||
|
() => set_value('currency', currency),
|
||||||
|
() => set_value('price_list_currency', currency),
|
||||||
|
() => set_value('status', 'Draft'),
|
||||||
|
() => set_value('is_subcontracted', 'No'),
|
||||||
|
() => {
|
||||||
|
if(this.frm.doc.company && !this.frm.doc.amended_from) {
|
||||||
|
this.frm.trigger("company");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setup_quality_inspection: function() {
|
setup_quality_inspection: function() {
|
||||||
@ -195,13 +199,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onload_post_render: function() {
|
onload_post_render: function() {
|
||||||
var me = this;
|
|
||||||
if(this.frm.doc.__islocal && !(this.frm.doc.taxes || []).length
|
if(this.frm.doc.__islocal && !(this.frm.doc.taxes || []).length
|
||||||
&& !(this.frm.doc.__onload ? this.frm.doc.__onload.load_after_mapping : false)) {
|
&& !(this.frm.doc.__onload ? this.frm.doc.__onload.load_after_mapping : false)) {
|
||||||
this.apply_default_taxes();
|
frappe.after_ajax(() => this.apply_default_taxes());
|
||||||
} else if(this.frm.doc.__islocal && this.frm.doc.company && this.frm.doc["items"]
|
} else if(this.frm.doc.__islocal && this.frm.doc.company && this.frm.doc["items"]
|
||||||
&& !this.frm.doc.is_pos) {
|
&& !this.frm.doc.is_pos) {
|
||||||
me.calculate_taxes_and_totals();
|
frappe.after_ajax(() => this.calculate_taxes_and_totals());
|
||||||
}
|
}
|
||||||
if(frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "item_code")) {
|
if(frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "item_code")) {
|
||||||
this.setup_item_selector();
|
this.setup_item_selector();
|
||||||
@ -378,6 +381,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
if(me.frm.doc.company && me.frm.fields_dict.currency) {
|
if(me.frm.doc.company && me.frm.fields_dict.currency) {
|
||||||
var company_currency = me.get_company_currency();
|
var company_currency = me.get_company_currency();
|
||||||
var company_doc = frappe.get_doc(":Company", me.frm.doc.company);
|
var company_doc = frappe.get_doc(":Company", me.frm.doc.company);
|
||||||
|
|
||||||
if (!me.frm.doc.currency) {
|
if (!me.frm.doc.currency) {
|
||||||
me.frm.set_value("currency", company_currency);
|
me.frm.set_value("currency", company_currency);
|
||||||
}
|
}
|
||||||
@ -519,6 +523,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
conversion_rate: function() {
|
conversion_rate: function() {
|
||||||
|
const me = this.frm;
|
||||||
if(this.frm.doc.currency === this.get_company_currency()) {
|
if(this.frm.doc.currency === this.get_company_currency()) {
|
||||||
this.frm.set_value("conversion_rate", 1.0);
|
this.frm.set_value("conversion_rate", 1.0);
|
||||||
}
|
}
|
||||||
@ -536,6 +541,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// Make read only if Accounts Settings doesn't allow stale rates
|
||||||
|
frappe.model.get_value("Accounts Settings", null, "allow_stale",
|
||||||
|
function(d){
|
||||||
|
me.set_df_property("conversion_rate", "read_only", cint(d.allow_stale) ? 0 : 1);
|
||||||
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
set_actual_charges_based_on_currency: function() {
|
set_actual_charges_based_on_currency: function() {
|
||||||
|
@ -86,6 +86,10 @@ erpnext.setup.slides_settings = [
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
validate: function() {
|
validate: function() {
|
||||||
|
if ((this.values.company_name || "").toLowerCase() == "company") {
|
||||||
|
frappe.msgprint(__("Company Name cannot be Company"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!this.values.company_abbr) {
|
if (!this.values.company_abbr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -135,10 +139,6 @@ erpnext.setup.slides_settings = [
|
|||||||
frappe.msgprint(__("Please enter valid Financial Year Start and End Dates"));
|
frappe.msgprint(__("Please enter valid Financial Year Start and End Dates"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ((this.values.company_name || "").toLowerCase() == "company") {
|
|
||||||
frappe.msgprint(__("Company Name cannot be Company"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ def setup(company=None, patch=True):
|
|||||||
make_custom_fields()
|
make_custom_fields()
|
||||||
add_permissions()
|
add_permissions()
|
||||||
add_custom_roles_for_reports()
|
add_custom_roles_for_reports()
|
||||||
frappe.enqueue('erpnext.regional.india.setup.add_hsn_sac_codes')
|
frappe.enqueue('erpnext.regional.india.setup.add_hsn_sac_codes', now=frappe.flags.in_test)
|
||||||
add_print_formats()
|
add_print_formats()
|
||||||
if not patch:
|
if not patch:
|
||||||
update_address_template()
|
update_address_template()
|
||||||
|
@ -13,7 +13,6 @@ class StudentApplicant(Document):
|
|||||||
if self.student_admission:
|
if self.student_admission:
|
||||||
naming_series = frappe.db.get_value('Student Admission', self.student_admission,
|
naming_series = frappe.db.get_value('Student Admission', self.student_admission,
|
||||||
'naming_series_for_student_applicant')
|
'naming_series_for_student_applicant')
|
||||||
print(naming_series)
|
|
||||||
|
|
||||||
if naming_series:
|
if naming_series:
|
||||||
self.naming_series = naming_series
|
self.naming_series = naming_series
|
||||||
|
@ -12,8 +12,8 @@ from erpnext.stock.utils import get_valid_serial_nos
|
|||||||
from erpnext.utilities.transaction_base import TransactionBase
|
from erpnext.utilities.transaction_base import TransactionBase
|
||||||
|
|
||||||
class InstallationNote(TransactionBase):
|
class InstallationNote(TransactionBase):
|
||||||
def __init__(self, arg1, arg2=None):
|
def __init__(self, *args, **kwargs):
|
||||||
super(InstallationNote, self).__init__(arg1, arg2)
|
super(InstallationNote, self).__init__(*args, **kwargs)
|
||||||
self.status_updater = [{
|
self.status_updater = [{
|
||||||
'source_dt': 'Installation Note Item',
|
'source_dt': 'Installation Note Item',
|
||||||
'target_dt': 'Delivery Note Item',
|
'target_dt': 'Delivery Note Item',
|
||||||
|
@ -32,7 +32,7 @@ class Quotation(SellingController):
|
|||||||
self.validate_valid_till()
|
self.validate_valid_till()
|
||||||
if self.items:
|
if self.items:
|
||||||
self.with_items = 1
|
self.with_items = 1
|
||||||
|
|
||||||
def validate_valid_till(self):
|
def validate_valid_till(self):
|
||||||
if self.valid_till and self.valid_till < self.transaction_date:
|
if self.valid_till and self.valid_till < self.transaction_date:
|
||||||
frappe.throw(_("Valid till date cannot be before transaction date"))
|
frappe.throw(_("Valid till date cannot be before transaction date"))
|
||||||
@ -79,15 +79,10 @@ class Quotation(SellingController):
|
|||||||
else:
|
else:
|
||||||
frappe.throw(_("Cannot set as Lost as Sales Order is made."))
|
frappe.throw(_("Cannot set as Lost as Sales Order is made."))
|
||||||
|
|
||||||
def check_item_table(self):
|
|
||||||
if not self.get('items'):
|
|
||||||
frappe.throw(_("Please enter item details"))
|
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.check_item_table()
|
|
||||||
|
|
||||||
# Check for Approving Authority
|
# Check for Approving Authority
|
||||||
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.company, self.base_grand_total, self)
|
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
||||||
|
self.company, self.base_grand_total, self)
|
||||||
|
|
||||||
#update enquiry status
|
#update enquiry status
|
||||||
self.update_opportunity()
|
self.update_opportunity()
|
||||||
|
@ -22,8 +22,8 @@ form_grid_templates = {
|
|||||||
class WarehouseRequired(frappe.ValidationError): pass
|
class WarehouseRequired(frappe.ValidationError): pass
|
||||||
|
|
||||||
class SalesOrder(SellingController):
|
class SalesOrder(SellingController):
|
||||||
def __init__(self, arg1, arg2=None):
|
def __init__(self, *args, **kwargs):
|
||||||
super(SalesOrder, self).__init__(arg1, arg2)
|
super(SalesOrder, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
super(SalesOrder, self).validate()
|
super(SalesOrder, self).validate()
|
||||||
@ -696,7 +696,8 @@ def make_purchase_order_for_drop_shipment(source_name, for_supplier, target_doc=
|
|||||||
"contact_display",
|
"contact_display",
|
||||||
"contact_mobile",
|
"contact_mobile",
|
||||||
"contact_email",
|
"contact_email",
|
||||||
"contact_person"
|
"contact_person",
|
||||||
|
"taxes_and_charges"
|
||||||
],
|
],
|
||||||
"validation": {
|
"validation": {
|
||||||
"docstatus": ["=", 1]
|
"docstatus": ["=", 1]
|
||||||
|
@ -494,7 +494,7 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
so.items[0].price_list_rate = price_list_rate = 100
|
so.items[0].price_list_rate = price_list_rate = 100
|
||||||
so.items[0].margin_type = 'Percentage'
|
so.items[0].margin_type = 'Percentage'
|
||||||
so.items[0].margin_rate_or_amount = 25
|
so.items[0].margin_rate_or_amount = 25
|
||||||
so.insert()
|
so.save()
|
||||||
|
|
||||||
new_so = frappe.copy_doc(so)
|
new_so = frappe.copy_doc(so)
|
||||||
new_so.save(ignore_permissions=True)
|
new_so.save(ignore_permissions=True)
|
||||||
|
@ -75,11 +75,7 @@ class Company(Document):
|
|||||||
if not frappe.local.flags.ignore_chart_of_accounts:
|
if not frappe.local.flags.ignore_chart_of_accounts:
|
||||||
self.create_default_accounts()
|
self.create_default_accounts()
|
||||||
self.create_default_warehouses()
|
self.create_default_warehouses()
|
||||||
|
install_country_fixtures(self.name)
|
||||||
if cint(frappe.db.get_single_value('System Settings', 'setup_complete')):
|
|
||||||
# In the case of setup, fixtures should be installed after setup_success
|
|
||||||
# This also prevents db commits before setup is successful
|
|
||||||
install_country_fixtures(self.name)
|
|
||||||
|
|
||||||
if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": self.name}):
|
if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": self.name}):
|
||||||
self.create_default_cost_center()
|
self.create_default_cost_center()
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|
||||||
import frappe, unittest
|
import frappe, unittest
|
||||||
|
from erpnext.setup.utils import get_exchange_rate
|
||||||
|
|
||||||
test_records = frappe.get_test_records('Currency Exchange')
|
test_records = frappe.get_test_records('Currency Exchange')
|
||||||
|
|
||||||
|
|
||||||
@ -28,11 +28,21 @@ def save_new_records(test_records):
|
|||||||
|
|
||||||
|
|
||||||
class TestCurrencyExchange(unittest.TestCase):
|
class TestCurrencyExchange(unittest.TestCase):
|
||||||
def test_exchnage_rate(self):
|
def clear_cache(self):
|
||||||
from erpnext.setup.utils import get_exchange_rate
|
cache = frappe.cache()
|
||||||
|
key = "currency_exchange_rate:{0}:{1}".format("USD", "INR")
|
||||||
|
cache.delete(key)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
frappe.db.set_value("Accounts Settings", None, "allow_stale", 1)
|
||||||
|
self.clear_cache()
|
||||||
|
|
||||||
|
def test_exchange_rate(self):
|
||||||
save_new_records(test_records)
|
save_new_records(test_records)
|
||||||
|
|
||||||
|
frappe.db.set_value("Accounts Settings", None, "allow_stale", 1)
|
||||||
|
|
||||||
|
# Start with allow_stale is True
|
||||||
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-01")
|
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-01")
|
||||||
self.assertEqual(exchange_rate, 60.0)
|
self.assertEqual(exchange_rate, 60.0)
|
||||||
|
|
||||||
@ -43,6 +53,51 @@ class TestCurrencyExchange(unittest.TestCase):
|
|||||||
self.assertEqual(exchange_rate, 62.9)
|
self.assertEqual(exchange_rate, 62.9)
|
||||||
|
|
||||||
# Exchange rate as on 15th Dec, 2015, should be fetched from fixer.io
|
# Exchange rate as on 15th Dec, 2015, should be fetched from fixer.io
|
||||||
|
self.clear_cache()
|
||||||
exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15")
|
exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15")
|
||||||
self.assertFalse(exchange_rate == 60)
|
self.assertFalse(exchange_rate == 60)
|
||||||
self.assertEqual(exchange_rate, 66.894)
|
self.assertEqual(exchange_rate, 66.894)
|
||||||
|
|
||||||
|
def test_exchange_rate_strict(self):
|
||||||
|
# strict currency settings
|
||||||
|
frappe.db.set_value("Accounts Settings", None, "allow_stale", 0)
|
||||||
|
frappe.db.set_value("Accounts Settings", None, "stale_days", 1)
|
||||||
|
|
||||||
|
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-01")
|
||||||
|
self.assertEqual(exchange_rate, 60.0)
|
||||||
|
|
||||||
|
# Will fetch from fixer.io
|
||||||
|
self.clear_cache()
|
||||||
|
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15")
|
||||||
|
self.assertEqual(exchange_rate, 67.79)
|
||||||
|
|
||||||
|
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30")
|
||||||
|
self.assertEqual(exchange_rate, 62.9)
|
||||||
|
|
||||||
|
# Exchange rate as on 15th Dec, 2015, should be fetched from fixer.io
|
||||||
|
self.clear_cache()
|
||||||
|
exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15")
|
||||||
|
self.assertEqual(exchange_rate, 66.894)
|
||||||
|
|
||||||
|
exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-10")
|
||||||
|
self.assertEqual(exchange_rate, 65.1)
|
||||||
|
|
||||||
|
# NGN is not available on fixer.io so these should return 0
|
||||||
|
exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-09")
|
||||||
|
self.assertEqual(exchange_rate, 0)
|
||||||
|
|
||||||
|
exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-11")
|
||||||
|
self.assertEqual(exchange_rate, 0)
|
||||||
|
|
||||||
|
def test_exchange_rate_strict_switched(self):
|
||||||
|
# Start with allow_stale is True
|
||||||
|
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15")
|
||||||
|
self.assertEqual(exchange_rate, 65.1)
|
||||||
|
|
||||||
|
frappe.db.set_value("Accounts Settings", None, "allow_stale", 0)
|
||||||
|
frappe.db.set_value("Accounts Settings", None, "stale_days", 1)
|
||||||
|
|
||||||
|
# Will fetch from fixer.io
|
||||||
|
self.clear_cache()
|
||||||
|
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15")
|
||||||
|
self.assertEqual(exchange_rate, 67.79)
|
@ -33,5 +33,12 @@
|
|||||||
"exchange_rate": 62.9,
|
"exchange_rate": 62.9,
|
||||||
"from_currency": "USD",
|
"from_currency": "USD",
|
||||||
"to_currency": "INR"
|
"to_currency": "INR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"doctype": "Currency Exchange",
|
||||||
|
"date": "2016-01-10",
|
||||||
|
"exchange_rate": 65.1,
|
||||||
|
"from_currency": "INR",
|
||||||
|
"to_currency": "NGN"
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -16,14 +16,13 @@ user_specific_content = ["calendar_events", "todo_list"]
|
|||||||
|
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
class EmailDigest(Document):
|
class EmailDigest(Document):
|
||||||
def __init__(self, arg1, arg2=None):
|
def __init__(self, *args, **kwargs):
|
||||||
super(EmailDigest, self).__init__(arg1, arg2)
|
super(EmailDigest, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.from_date, self.to_date = self.get_from_to_date()
|
self.from_date, self.to_date = self.get_from_to_date()
|
||||||
self.set_dates()
|
self.set_dates()
|
||||||
self._accounts = {}
|
self._accounts = {}
|
||||||
self.currency = frappe.db.get_value("Company", self.company,
|
self.currency = frappe.db.get_value("Company", self.company, "default_currency")
|
||||||
"default_currency")
|
|
||||||
|
|
||||||
def get_users(self):
|
def get_users(self):
|
||||||
"""get list of users"""
|
"""get list of users"""
|
||||||
|
@ -33,6 +33,7 @@ def setup_complete(args=None):
|
|||||||
create_feed_and_todo()
|
create_feed_and_todo()
|
||||||
create_email_digest()
|
create_email_digest()
|
||||||
create_letter_head(args)
|
create_letter_head(args)
|
||||||
|
set_no_copy_fields_in_variant_settings()
|
||||||
|
|
||||||
if args.get('domain').lower() == 'education':
|
if args.get('domain').lower() == 'education':
|
||||||
create_academic_year()
|
create_academic_year()
|
||||||
@ -65,10 +66,6 @@ def setup_complete(args=None):
|
|||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def setup_success(args=None):
|
|
||||||
company = frappe.db.sql("select name from tabCompany", as_dict=True)[0]["name"]
|
|
||||||
install_country_fixtures(company)
|
|
||||||
|
|
||||||
def create_fiscal_year_and_company(args):
|
def create_fiscal_year_and_company(args):
|
||||||
if (args.get('fy_start_date')):
|
if (args.get('fy_start_date')):
|
||||||
curr_fiscal_year = get_fy_details(args.get('fy_start_date'), args.get('fy_end_date'))
|
curr_fiscal_year = get_fy_details(args.get('fy_start_date'), args.get('fy_end_date'))
|
||||||
@ -354,6 +351,12 @@ def create_letter_head(args):
|
|||||||
fileurl = save_file(filename, content, "Letter Head", _("Standard"), decode=True).file_url
|
fileurl = save_file(filename, content, "Letter Head", _("Standard"), decode=True).file_url
|
||||||
frappe.db.set_value("Letter Head", _("Standard"), "content", "<img src='%s' style='max-width: 100%%;'>" % fileurl)
|
frappe.db.set_value("Letter Head", _("Standard"), "content", "<img src='%s' style='max-width: 100%%;'>" % fileurl)
|
||||||
|
|
||||||
|
def set_no_copy_fields_in_variant_settings():
|
||||||
|
# set no copy fields of an item doctype to item variant settings
|
||||||
|
doc = frappe.get_doc('Item Variant Settings')
|
||||||
|
doc.set_default_fields()
|
||||||
|
doc.save()
|
||||||
|
|
||||||
def create_logo(args):
|
def create_logo(args):
|
||||||
if args.get("attach_logo"):
|
if args.get("attach_logo"):
|
||||||
attach_logo = args.get("attach_logo").split(",")
|
attach_logo = args.get("attach_logo").split(",")
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt, add_days
|
||||||
from frappe.utils import get_datetime_str, nowdate
|
from frappe.utils import get_datetime_str, nowdate
|
||||||
|
|
||||||
def get_root_of(doctype):
|
def get_root_of(doctype):
|
||||||
@ -56,8 +56,6 @@ def before_tests():
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_exchange_rate(from_currency, to_currency, transaction_date=None):
|
def get_exchange_rate(from_currency, to_currency, transaction_date=None):
|
||||||
if not transaction_date:
|
|
||||||
transaction_date = nowdate()
|
|
||||||
if not (from_currency and to_currency):
|
if not (from_currency and to_currency):
|
||||||
# manqala 19/09/2016: Should this be an empty return or should it throw and exception?
|
# manqala 19/09/2016: Should this be an empty return or should it throw and exception?
|
||||||
return
|
return
|
||||||
@ -65,13 +63,27 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None):
|
|||||||
if from_currency == to_currency:
|
if from_currency == to_currency:
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
if not transaction_date:
|
||||||
|
transaction_date = nowdate()
|
||||||
|
|
||||||
|
currency_settings = frappe.get_doc("Accounts Settings").as_dict()
|
||||||
|
allow_stale_rates = currency_settings.get("allow_stale")
|
||||||
|
|
||||||
|
filters = [
|
||||||
|
["date", "<=", get_datetime_str(transaction_date)],
|
||||||
|
["from_currency", "=", from_currency],
|
||||||
|
["to_currency", "=", to_currency]
|
||||||
|
]
|
||||||
|
|
||||||
|
if not allow_stale_rates:
|
||||||
|
stale_days = currency_settings.get("stale_days")
|
||||||
|
checkpoint_date = add_days(transaction_date, -stale_days)
|
||||||
|
filters.append(["date", ">", get_datetime_str(checkpoint_date)])
|
||||||
|
|
||||||
# cksgb 19/09/2016: get last entry in Currency Exchange with from_currency and to_currency.
|
# cksgb 19/09/2016: get last entry in Currency Exchange with from_currency and to_currency.
|
||||||
entries = frappe.get_all("Currency Exchange", fields = ["exchange_rate"],
|
entries = frappe.get_all(
|
||||||
filters=[
|
"Currency Exchange", fields=["exchange_rate"], filters=filters, order_by="date desc",
|
||||||
["date", "<=", get_datetime_str(transaction_date)],
|
limit=1)
|
||||||
["from_currency", "=", from_currency],
|
|
||||||
["to_currency", "=", to_currency]
|
|
||||||
], order_by="date desc", limit=1)
|
|
||||||
|
|
||||||
if entries:
|
if entries:
|
||||||
return flt(entries[0].exchange_rate)
|
return flt(entries[0].exchange_rate)
|
||||||
@ -108,8 +120,9 @@ def enable_all_roles_and_domains():
|
|||||||
_role.save()
|
_role.save()
|
||||||
|
|
||||||
# add all roles to users
|
# add all roles to users
|
||||||
user = frappe.get_doc("User", "Administrator")
|
if roles:
|
||||||
user.add_roles(*[role.get("name") for role in roles])
|
user = frappe.get_doc("User", "Administrator")
|
||||||
|
user.add_roles(*[role.get("name") for role in roles])
|
||||||
|
|
||||||
domains = frappe.get_list("Domain")
|
domains = frappe.get_list("Domain")
|
||||||
if not domains:
|
if not domains:
|
||||||
|
@ -21,8 +21,8 @@ form_grid_templates = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DeliveryNote(SellingController):
|
class DeliveryNote(SellingController):
|
||||||
def __init__(self, arg1, arg2=None):
|
def __init__(self, *args, **kwargs):
|
||||||
super(DeliveryNote, self).__init__(arg1, arg2)
|
super(DeliveryNote, self).__init__(*args, **kwargs)
|
||||||
self.status_updater = [{
|
self.status_updater = [{
|
||||||
'source_dt': 'Delivery Note Item',
|
'source_dt': 'Delivery Note Item',
|
||||||
'target_dt': 'Sales Order Item',
|
'target_dt': 'Sales Order Item',
|
||||||
|
@ -97,6 +97,12 @@ frappe.ui.form.on("Item", {
|
|||||||
}
|
}
|
||||||
frappe.set_route('Form', 'Item', new_item.name);
|
frappe.set_route('Form', 'Item', new_item.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(frm.doc.has_variants) {
|
||||||
|
frm.add_custom_button(__("Item Variant Settings"), function() {
|
||||||
|
frappe.set_route("Form", "Item Variant Settings");
|
||||||
|
}, __("View"));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
validate: function(frm){
|
validate: function(frm){
|
||||||
|
@ -1473,6 +1473,37 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 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_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -2069,6 +2100,37 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 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_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -3143,7 +3205,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 1,
|
"max_attachments": 1,
|
||||||
"modified": "2017-07-06 18:28:36.645217",
|
"modified": "2017-09-27 14:08:02.948326",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Item",
|
"name": "Item",
|
||||||
|
@ -100,6 +100,7 @@ class Item(WebsiteGenerator):
|
|||||||
def on_update(self):
|
def on_update(self):
|
||||||
invalidate_cache_for_item(self)
|
invalidate_cache_for_item(self)
|
||||||
self.validate_name_with_item_group()
|
self.validate_name_with_item_group()
|
||||||
|
self.update_variants()
|
||||||
self.update_item_price()
|
self.update_item_price()
|
||||||
self.update_template_item()
|
self.update_template_item()
|
||||||
|
|
||||||
@ -607,9 +608,24 @@ class Item(WebsiteGenerator):
|
|||||||
|
|
||||||
if not template_item.show_in_website:
|
if not template_item.show_in_website:
|
||||||
template_item.show_in_website = 1
|
template_item.show_in_website = 1
|
||||||
|
template_item.flags.dont_update_variants = True
|
||||||
template_item.flags.ignore_permissions = True
|
template_item.flags.ignore_permissions = True
|
||||||
template_item.save()
|
template_item.save()
|
||||||
|
|
||||||
|
def update_variants(self):
|
||||||
|
if self.flags.dont_update_variants:
|
||||||
|
return
|
||||||
|
if self.has_variants:
|
||||||
|
updated = []
|
||||||
|
variants = frappe.db.get_all("Item", fields=["item_code"], filters={"variant_of": self.name })
|
||||||
|
for d in variants:
|
||||||
|
variant = frappe.get_doc("Item", d)
|
||||||
|
copy_attributes_to_variant(self, variant)
|
||||||
|
variant.save()
|
||||||
|
updated.append(d.item_code)
|
||||||
|
if updated:
|
||||||
|
frappe.msgprint(_("Item Variants {0} updated").format(", ".join(updated)))
|
||||||
|
|
||||||
def validate_has_variants(self):
|
def validate_has_variants(self):
|
||||||
if not self.has_variants and frappe.db.get_value("Item", self.name, "has_variants"):
|
if not self.has_variants and frappe.db.get_value("Item", self.name, "has_variants"):
|
||||||
if frappe.db.exists("Item", {"variant_of": self.name}):
|
if frappe.db.exists("Item", {"variant_of": self.name}):
|
||||||
|
@ -119,6 +119,39 @@ class TestItem(unittest.TestCase):
|
|||||||
variant.item_code = "_Test Variant Item-L-duplicate"
|
variant.item_code = "_Test Variant Item-L-duplicate"
|
||||||
self.assertRaises(ItemVariantExistsError, variant.save)
|
self.assertRaises(ItemVariantExistsError, variant.save)
|
||||||
|
|
||||||
|
def test_copy_fields_from_template_to_variants(self):
|
||||||
|
frappe.delete_doc_if_exists("Item", "_Test Variant Item-XL", force=1)
|
||||||
|
|
||||||
|
fields = [{'field_name': 'item_group'}, {'field_name': 'is_stock_item'}]
|
||||||
|
allow_fields = [d.get('field_name') for d in fields]
|
||||||
|
set_item_variant_settings(fields)
|
||||||
|
|
||||||
|
if not frappe.db.get_value('Item Attribute Value',
|
||||||
|
{'parent': 'Test Size', 'attribute_value': 'Extra Large'}, 'name'):
|
||||||
|
item_attribute = frappe.get_doc('Item Attribute', 'Test Size')
|
||||||
|
item_attribute.append('item_attribute_values', {
|
||||||
|
'attribute_value' : 'Extra Large',
|
||||||
|
'abbr': 'XL'
|
||||||
|
})
|
||||||
|
item_attribute.save()
|
||||||
|
|
||||||
|
variant = create_variant("_Test Variant Item", {"Test Size": "Extra Large"})
|
||||||
|
variant.item_code = "_Test Variant Item-XL"
|
||||||
|
variant.item_name = "_Test Variant Item-XL"
|
||||||
|
variant.save()
|
||||||
|
|
||||||
|
template = frappe.get_doc('Item', '_Test Variant Item')
|
||||||
|
template.item_group = "_Test Item Group D"
|
||||||
|
template.save()
|
||||||
|
|
||||||
|
variant = frappe.get_doc('Item', '_Test Variant Item-XL')
|
||||||
|
for fieldname in allow_fields:
|
||||||
|
self.assertEquals(template.get(fieldname), variant.get(fieldname))
|
||||||
|
|
||||||
|
template = frappe.get_doc('Item', '_Test Variant Item')
|
||||||
|
template.item_group = "_Test Item Group Desktops"
|
||||||
|
template.save()
|
||||||
|
|
||||||
def test_make_item_variant_with_numeric_values(self):
|
def test_make_item_variant_with_numeric_values(self):
|
||||||
# cleanup
|
# cleanup
|
||||||
for d in frappe.db.get_all('Item', filters={'variant_of':
|
for d in frappe.db.get_all('Item', filters={'variant_of':
|
||||||
@ -194,6 +227,9 @@ class TestItem(unittest.TestCase):
|
|||||||
{"item_code": "Test Item for Merging 2", "warehouse": "_Test Warehouse 1 - _TC"}))
|
{"item_code": "Test Item for Merging 2", "warehouse": "_Test Warehouse 1 - _TC"}))
|
||||||
|
|
||||||
def test_item_variant_by_manufacturer(self):
|
def test_item_variant_by_manufacturer(self):
|
||||||
|
fields = [{'field_name': 'description'}, {'field_name': 'variant_based_on'}]
|
||||||
|
set_item_variant_settings(fields)
|
||||||
|
|
||||||
if frappe.db.exists('Item', '_Test Variant Mfg'):
|
if frappe.db.exists('Item', '_Test Variant Mfg'):
|
||||||
frappe.delete_doc('Item', '_Test Variant Mfg')
|
frappe.delete_doc('Item', '_Test Variant Mfg')
|
||||||
if frappe.db.exists('Item', '_Test Variant Mfg-1'):
|
if frappe.db.exists('Item', '_Test Variant Mfg-1'):
|
||||||
@ -227,6 +263,10 @@ class TestItem(unittest.TestCase):
|
|||||||
self.assertEquals(variant.manufacturer, 'MSG1')
|
self.assertEquals(variant.manufacturer, 'MSG1')
|
||||||
self.assertEquals(variant.manufacturer_part_no, '007')
|
self.assertEquals(variant.manufacturer_part_no, '007')
|
||||||
|
|
||||||
|
def set_item_variant_settings(fields):
|
||||||
|
doc = frappe.get_doc('Item Variant Settings')
|
||||||
|
doc.set('fields', fields)
|
||||||
|
doc.save()
|
||||||
|
|
||||||
def make_item_variant():
|
def make_item_variant():
|
||||||
if not frappe.db.exists("Item", "_Test Variant Item-S"):
|
if not frappe.db.exists("Item", "_Test Variant Item-S"):
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_copy": 0,
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 0,
|
"allow_rename": 0,
|
||||||
"autoname": "ITEM-PRICE-.#####",
|
"autoname": "ITEM-PRICE-.#####",
|
||||||
@ -165,7 +165,7 @@
|
|||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 0,
|
||||||
"label": "",
|
"label": "",
|
||||||
@ -357,19 +357,19 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_heading": 0,
|
"hide_heading": 0,
|
||||||
"hide_toolbar": 0,
|
"hide_toolbar": 0,
|
||||||
"icon": "fa fa-flag",
|
"icon": "fa fa-flag",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"image_view": 0,
|
"image_view": 0,
|
||||||
"in_create": 0,
|
"in_create": 0,
|
||||||
"in_dialog": 0,
|
"in_dialog": 0,
|
||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2017-02-20 13:27:23.896148",
|
"modified": "2017-09-28 03:56:20.814993",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Item Price",
|
"name": "Item Price",
|
||||||
@ -422,6 +422,6 @@
|
|||||||
"show_name_in_global_search": 0,
|
"show_name_in_global_search": 0,
|
||||||
"sort_order": "ASC",
|
"sort_order": "ASC",
|
||||||
"title_field": "item_code",
|
"title_field": "item_code",
|
||||||
"track_changes": 0,
|
"track_changes": 1,
|
||||||
"track_seen": 0
|
"track_seen": 0
|
||||||
}
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('Item Variant Settings', {
|
||||||
|
setup: function(frm) {
|
||||||
|
const allow_fields = [];
|
||||||
|
const exclude_fields = ["naming_series", "item_code", "item_name", "show_in_website",
|
||||||
|
"show_variant_in_website", "opening_stock", "variant_of", "valuation_rate"];
|
||||||
|
|
||||||
|
frappe.model.with_doctype('Item', () => {
|
||||||
|
frappe.get_meta('Item').fields.forEach(d => {
|
||||||
|
if(!in_list(['HTML', 'Section Break', 'Column Break', 'Button', 'Read Only'], d.fieldtype)
|
||||||
|
&& !d.no_copy && !in_list(exclude_fields, d.fieldname)) {
|
||||||
|
allow_fields.push(d.fieldname);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const child = frappe.meta.get_docfield("Variant Field", "field_name", frm.doc.name);
|
||||||
|
child.options = allow_fields;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,143 @@
|
|||||||
|
{
|
||||||
|
"allow_copy": 0,
|
||||||
|
"allow_guest_to_view": 0,
|
||||||
|
"allow_import": 0,
|
||||||
|
"allow_rename": 0,
|
||||||
|
"beta": 0,
|
||||||
|
"creation": "2017-08-29 16:38:31.173830",
|
||||||
|
"custom": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "DocType",
|
||||||
|
"document_type": "",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "copy_fields_to_variant",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"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": "Copy Fields to Variant",
|
||||||
|
"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,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "fields",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"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": "Fields",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Variant Field",
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"has_web_view": 0,
|
||||||
|
"hide_heading": 0,
|
||||||
|
"hide_toolbar": 0,
|
||||||
|
"idx": 0,
|
||||||
|
"image_view": 0,
|
||||||
|
"in_create": 0,
|
||||||
|
"is_submittable": 0,
|
||||||
|
"issingle": 1,
|
||||||
|
"istable": 0,
|
||||||
|
"max_attachments": 0,
|
||||||
|
"modified": "2017-09-11 12:05:16.288601",
|
||||||
|
"modified_by": "rohit@erpnext.com",
|
||||||
|
"module": "Stock",
|
||||||
|
"name": "Item Variant Settings",
|
||||||
|
"name_case": "",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
|
"cancel": 0,
|
||||||
|
"create": 1,
|
||||||
|
"delete": 0,
|
||||||
|
"email": 1,
|
||||||
|
"export": 0,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 0,
|
||||||
|
"role": "System Manager",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"share": 1,
|
||||||
|
"submit": 0,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
|
"cancel": 0,
|
||||||
|
"create": 1,
|
||||||
|
"delete": 0,
|
||||||
|
"email": 1,
|
||||||
|
"export": 0,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 0,
|
||||||
|
"role": "Item Manager",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"share": 1,
|
||||||
|
"submit": 0,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0,
|
||||||
|
"show_name_in_global_search": 0,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1,
|
||||||
|
"track_seen": 0
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class ItemVariantSettings(Document):
|
||||||
|
def set_default_fields(self):
|
||||||
|
self.fields = []
|
||||||
|
fields = frappe.get_meta('Item').fields
|
||||||
|
exclude_fields = ["naming_series", "item_code", "item_name", "show_in_website",
|
||||||
|
"show_variant_in_website", "standard_rate", "opening_stock", "image", "description",
|
||||||
|
"variant_of", "valuation_rate", "description",
|
||||||
|
"website_image", "thumbnail", "website_specifiations", "web_long_description"]
|
||||||
|
|
||||||
|
for d in fields:
|
||||||
|
if not d.no_copy and d.fieldname not in exclude_fields and \
|
||||||
|
d.fieldtype not in ['HTML', 'Section Break', 'Column Break', 'Button', 'Read Only']:
|
||||||
|
self.append('fields', {
|
||||||
|
'field_name': d.fieldname
|
||||||
|
})
|
@ -0,0 +1,23 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// rename this file from _test_[name] to test_[name] to activate
|
||||||
|
// and remove above this line
|
||||||
|
|
||||||
|
QUnit.test("test: Item Variant Settings", function (assert) {
|
||||||
|
let done = assert.async();
|
||||||
|
|
||||||
|
// number of asserts
|
||||||
|
assert.expect(1);
|
||||||
|
|
||||||
|
frappe.run_serially([
|
||||||
|
// insert a new Item Variant Settings
|
||||||
|
() => frappe.tests.make('Item Variant Settings', [
|
||||||
|
// values to be set
|
||||||
|
{key: 'value'}
|
||||||
|
]),
|
||||||
|
() => {
|
||||||
|
assert.equal(cur_frm.doc.key, 'value');
|
||||||
|
},
|
||||||
|
() => done()
|
||||||
|
]);
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,9 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# See license.txt
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class TestItemVariantSettings(unittest.TestCase):
|
||||||
|
pass
|
@ -19,8 +19,8 @@ form_grid_templates = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PurchaseReceipt(BuyingController):
|
class PurchaseReceipt(BuyingController):
|
||||||
def __init__(self, arg1, arg2=None):
|
def __init__(self, *args, **kwargs):
|
||||||
super(PurchaseReceipt, self).__init__(arg1, arg2)
|
super(PurchaseReceipt, self).__init__(*args, **kwargs)
|
||||||
self.status_updater = [{
|
self.status_updater = [{
|
||||||
'source_dt': 'Purchase Receipt Item',
|
'source_dt': 'Purchase Receipt Item',
|
||||||
'target_dt': 'Purchase Order Item',
|
'target_dt': 'Purchase Order Item',
|
||||||
|
@ -20,8 +20,8 @@ class SerialNoNotExistsError(ValidationError): pass
|
|||||||
class SerialNoDuplicateError(ValidationError): pass
|
class SerialNoDuplicateError(ValidationError): pass
|
||||||
|
|
||||||
class SerialNo(StockController):
|
class SerialNo(StockController):
|
||||||
def __init__(self, arg1, arg2=None):
|
def __init__(self, *args, **kwargs):
|
||||||
super(SerialNo, self).__init__(arg1, arg2)
|
super(SerialNo, self).__init__(*args, **kwargs)
|
||||||
self.via_stock_ledger = False
|
self.via_stock_ledger = False
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
@ -11,6 +11,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
|
|||||||
from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError
|
from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError
|
||||||
from erpnext.stock.stock_ledger import get_previous_sle
|
from erpnext.stock.stock_ledger import get_previous_sle
|
||||||
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
|
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
|
||||||
|
from erpnext.stock.doctype.item.test_item import set_item_variant_settings, make_item_variant
|
||||||
from frappe.tests.test_permissions import set_user_permission_doctypes
|
from frappe.tests.test_permissions import set_user_permission_doctypes
|
||||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||||
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
||||||
@ -45,6 +46,7 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
|
|
||||||
make_stock_entry(item_code=item_code, target=warehouse, qty=1, basic_rate=10)
|
make_stock_entry(item_code=item_code, target=warehouse, qty=1, basic_rate=10)
|
||||||
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
||||||
|
|
||||||
self.assertEqual([[1, 10]], frappe.safe_eval(sle.stock_queue))
|
self.assertEqual([[1, 10]], frappe.safe_eval(sle.stock_queue))
|
||||||
|
|
||||||
# negative qty
|
# negative qty
|
||||||
@ -73,12 +75,25 @@ class TestStockEntry(unittest.TestCase):
|
|||||||
frappe.db.set_default("allow_negative_stock", 0)
|
frappe.db.set_default("allow_negative_stock", 0)
|
||||||
|
|
||||||
def test_auto_material_request(self):
|
def test_auto_material_request(self):
|
||||||
from erpnext.stock.doctype.item.test_item import make_item_variant
|
|
||||||
make_item_variant()
|
make_item_variant()
|
||||||
self._test_auto_material_request("_Test Item")
|
self._test_auto_material_request("_Test Item")
|
||||||
self._test_auto_material_request("_Test Item", material_request_type="Transfer")
|
self._test_auto_material_request("_Test Item", material_request_type="Transfer")
|
||||||
|
|
||||||
def test_auto_material_request_for_variant(self):
|
def test_auto_material_request_for_variant(self):
|
||||||
|
fields = [{'field_name': 'reorder_levels'}]
|
||||||
|
set_item_variant_settings(fields)
|
||||||
|
make_item_variant()
|
||||||
|
template = frappe.get_doc("Item", "_Test Variant Item")
|
||||||
|
|
||||||
|
if not template.reorder_levels:
|
||||||
|
template.append('reorder_levels', {
|
||||||
|
"material_request_type": "Purchase",
|
||||||
|
"warehouse": "_Test Warehouse - _TC",
|
||||||
|
"warehouse_reorder_level": 20,
|
||||||
|
"warehouse_reorder_qty": 20
|
||||||
|
})
|
||||||
|
|
||||||
|
template.save()
|
||||||
self._test_auto_material_request("_Test Variant Item-S")
|
self._test_auto_material_request("_Test Variant Item-S")
|
||||||
|
|
||||||
def test_auto_material_request_for_warehouse_group(self):
|
def test_auto_material_request_for_warehouse_group(self):
|
||||||
|
@ -14,8 +14,8 @@ class OpeningEntryAccountError(frappe.ValidationError): pass
|
|||||||
class EmptyStockReconciliationItemsError(frappe.ValidationError): pass
|
class EmptyStockReconciliationItemsError(frappe.ValidationError): pass
|
||||||
|
|
||||||
class StockReconciliation(StockController):
|
class StockReconciliation(StockController):
|
||||||
def __init__(self, arg1, arg2=None):
|
def __init__(self, *args, **kwargs):
|
||||||
super(StockReconciliation, self).__init__(arg1, arg2)
|
super(StockReconciliation, self).__init__(*args, **kwargs)
|
||||||
self.head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"]
|
self.head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"]
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
0
erpnext/stock/doctype/variant_field/__init__.py
Normal file
0
erpnext/stock/doctype/variant_field/__init__.py
Normal file
23
erpnext/stock/doctype/variant_field/test_variant_field.js
Normal file
23
erpnext/stock/doctype/variant_field/test_variant_field.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// rename this file from _test_[name] to test_[name] to activate
|
||||||
|
// and remove above this line
|
||||||
|
|
||||||
|
QUnit.test("test: Variant Field", function (assert) {
|
||||||
|
let done = assert.async();
|
||||||
|
|
||||||
|
// number of asserts
|
||||||
|
assert.expect(1);
|
||||||
|
|
||||||
|
frappe.run_serially([
|
||||||
|
// insert a new Variant Field
|
||||||
|
() => frappe.tests.make('Variant Field', [
|
||||||
|
// values to be set
|
||||||
|
{key: 'value'}
|
||||||
|
]),
|
||||||
|
() => {
|
||||||
|
assert.equal(cur_frm.doc.key, 'value');
|
||||||
|
},
|
||||||
|
() => done()
|
||||||
|
]);
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,9 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# See license.txt
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class TestVariantField(unittest.TestCase):
|
||||||
|
pass
|
8
erpnext/stock/doctype/variant_field/variant_field.js
Normal file
8
erpnext/stock/doctype/variant_field/variant_field.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('Variant Field', {
|
||||||
|
refresh: function() {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
72
erpnext/stock/doctype/variant_field/variant_field.json
Normal file
72
erpnext/stock/doctype/variant_field/variant_field.json
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"allow_copy": 0,
|
||||||
|
"allow_guest_to_view": 0,
|
||||||
|
"allow_import": 0,
|
||||||
|
"allow_rename": 0,
|
||||||
|
"beta": 0,
|
||||||
|
"creation": "2017-08-29 16:33:33.978574",
|
||||||
|
"custom": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "DocType",
|
||||||
|
"document_type": "",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "field_name",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Field Name",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"has_web_view": 0,
|
||||||
|
"hide_heading": 0,
|
||||||
|
"hide_toolbar": 0,
|
||||||
|
"idx": 0,
|
||||||
|
"image_view": 0,
|
||||||
|
"in_create": 0,
|
||||||
|
"is_submittable": 0,
|
||||||
|
"issingle": 0,
|
||||||
|
"istable": 1,
|
||||||
|
"max_attachments": 0,
|
||||||
|
"modified": "2017-08-29 17:19:20.353197",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Stock",
|
||||||
|
"name": "Variant Field",
|
||||||
|
"name_case": "",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0,
|
||||||
|
"show_name_in_global_search": 0,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1,
|
||||||
|
"track_seen": 0
|
||||||
|
}
|
9
erpnext/stock/doctype/variant_field/variant_field.py
Normal file
9
erpnext/stock/doctype/variant_field/variant_field.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class VariantField(Document):
|
||||||
|
pass
|
@ -13,13 +13,6 @@
|
|||||||
"warehouse_name": "_Test Scrap Warehouse",
|
"warehouse_name": "_Test Scrap Warehouse",
|
||||||
"is_group": 0
|
"is_group": 0
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"company": "_Test Company",
|
|
||||||
"create_account_under": "Stock Assets - _TC",
|
|
||||||
"doctype": "Warehouse",
|
|
||||||
"warehouse_name": "_Test Warehouse",
|
|
||||||
"is_group": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"company": "_Test Company",
|
"company": "_Test Company",
|
||||||
"create_account_under": "Fixed Assets - _TC",
|
"create_account_under": "Fixed Assets - _TC",
|
||||||
|
@ -164,6 +164,15 @@ def get_basic_details(args, item):
|
|||||||
|
|
||||||
warehouse = user_default_warehouse or item.default_warehouse or args.warehouse
|
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 not args.uom:
|
||||||
|
if args.get('doctype') in ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']:
|
||||||
|
args.uom = item.sales_uom if item.sales_uom else item.stock_uom
|
||||||
|
elif args.get('doctype') in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']:
|
||||||
|
args.uom = item.purchase_uom if item.purchase_uom else item.stock_uom
|
||||||
|
else:
|
||||||
|
args.uom = item.stock_uom
|
||||||
|
|
||||||
out = frappe._dict({
|
out = frappe._dict({
|
||||||
"item_code": item.name,
|
"item_code": item.name,
|
||||||
"item_name": item.item_name,
|
"item_name": item.item_name,
|
||||||
@ -178,7 +187,7 @@ def get_basic_details(args, item):
|
|||||||
"batch_no": None,
|
"batch_no": None,
|
||||||
"item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in
|
"item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in
|
||||||
item.get("taxes")))),
|
item.get("taxes")))),
|
||||||
"uom": item.stock_uom,
|
"uom": args.uom,
|
||||||
"min_order_qty": flt(item.min_order_qty) if args.doctype == "Material Request" else "",
|
"min_order_qty": flt(item.min_order_qty) if args.doctype == "Material Request" else "",
|
||||||
"qty": args.qty or 1.0,
|
"qty": args.qty or 1.0,
|
||||||
"stock_qty": args.qty or 1.0,
|
"stock_qty": args.qty or 1.0,
|
||||||
|
@ -461,6 +461,6 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
|
|||||||
if not allow_zero_rate and not valuation_rate \
|
if not allow_zero_rate and not valuation_rate \
|
||||||
and cint(erpnext.is_perpetual_inventory_enabled(company)):
|
and cint(erpnext.is_perpetual_inventory_enabled(company)):
|
||||||
frappe.local.message_log = []
|
frappe.local.message_log = []
|
||||||
frappe.throw(_("Valuation rate not found for the Item {0}, which is required to do accounting entries for {1} {2}. If the item is transacting as a sample item in the {1}, please mention that in the {1} Item table. Otherwise, please create an incoming stock transaction for the item or mention valuation rate in the Item record, and then try submiting/cancelling this entry").format(item_code, voucher_type, voucher_no))
|
frappe.throw(_("Valuation rate not found for the Item {0}, which is required to do accounting entries for {1} {2}. If the item is transacting as a zero valuation rate item in the {1}, please mention that in the {1} Item table. Otherwise, please create an incoming stock transaction for the item or mention valuation rate in the Item record, and then try submiting/cancelling this entry").format(item_code, voucher_type, voucher_no))
|
||||||
|
|
||||||
return valuation_rate
|
return valuation_rate
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<h2>{{_("Recurring")}} {{ type }} {{ _("Failed")}}</h2>
|
<h2>{{_("Recurring")}} {{ type }} {{ _("Failed")}}</h2>
|
||||||
|
|
||||||
<p>An error occured while creating recurring {{ type }} <b>{{ name }}</b> for <b>{{ party }}</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>{{_("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>{{_("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><b>{{_("Please correct the")}} {{ type }} {{_('and unchcked "Disabled" in the')}} {{ subscription }} {{_("for making recurring again.")}}</b></p>
|
||||||
<hr>
|
<hr>
|
||||||
<p><b>It is necessary to take this action today itself for the above mentioned recurring {{ type }}
|
<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
|
{{_('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>
|
of this')}} {{ type }} {{_("for generating the recurring")}} {{ type }} {{_("in the subscription")}} {{ subscription }}.</b></p>
|
||||||
<p>[This email is autogenerated]</p>
|
<p>[{{_("This email is autogenerated")}}]</p>
|
||||||
|
@ -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/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/sales_invoice/tests/test_sales_invoice_with_serialize_item.js
|
||||||
erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.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
|
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
@ -6,7 +6,7 @@ DocType: Purchase Order,% Billed,% Billed
|
|||||||
,Lead Id,Bly Id
|
,Lead Id,Bly Id
|
||||||
apps/erpnext/erpnext/accounts/report/accounts_receivable/accounts_receivable.html +65,'Total','Total'
|
apps/erpnext/erpnext/accounts/report/accounts_receivable/accounts_receivable.html +65,'Total','Total'
|
||||||
DocType: Selling Settings,Selling Settings,Salg af indstillinger
|
DocType: Selling Settings,Selling Settings,Salg af indstillinger
|
||||||
apps/erpnext/erpnext/accounts/report/gross_profit/gross_profit.py +73,Selling Amount,Selling Beløb
|
apps/erpnext/erpnext/accounts/report/gross_profit/gross_profit.py +72,Selling Amount,Selling Beløb
|
||||||
apps/erpnext/erpnext/crm/doctype/opportunity/opportunity.py +174,Lead must be set if Opportunity is made from Lead,"Bly skal indstilles, hvis Opportunity er lavet af Lead"
|
apps/erpnext/erpnext/crm/doctype/opportunity/opportunity.py +174,Lead must be set if Opportunity is made from Lead,"Bly skal indstilles, hvis Opportunity er lavet af Lead"
|
||||||
DocType: Item,Default Selling Cost Center,Standard Selling Cost center
|
DocType: Item,Default Selling Cost Center,Standard Selling Cost center
|
||||||
apps/erpnext/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py +64,90-Above,90-Above
|
apps/erpnext/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py +64,90-Above,90-Above
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user