Compare commits

..

8 Commits

Author SHA1 Message Date
Brown-Harry Boma
40a43d3260 Replace create_custom_fields with create_custom_field (#11420) 2017-11-02 17:58:05 +05:30
Brown-Harry Boma
a829b3cc82 v8.x.x Allow Doctypes with space in name to be filtered in General Ledger (#11264)
* Set transaction type in pricing rule only if unavailable

* Use scrub instead for party_type with space
2017-10-25 11:58:45 +05:30
Brown-Harry Boma
757c2f692b [Fix]Setup Wizard Errors (#11289) 2017-10-25 11:52:08 +05:30
Brown-Harry Boma
85a9e2ed28 Check credit or debit in_account_currency is set before setting (#11253)
* Check credit or debit in_account_currency is set before setting

* Add paying party option to confirm custom doctype can pay
2017-10-21 11:25:40 +05:30
Brown-Harry Boma
f55a33890f Set transaction type in pricing rule only if unavailable (#11228) 2017-10-18 11:07:26 +05:30
Prateeksha Singh
ec992df81a [fix] Check for stock_qty, else use qty (#10937) 2017-09-27 18:33:01 +05:30
Prateeksha Singh
6f191eda99 [fix] batch qty checked against stock_qty field (#10906) 2017-09-27 15:34:23 +05:30
rohitwaghchaure
3318926b23 [hotfix] Wrong calculation of total in taxes and totals (#10924) 2017-09-27 13:32:27 +05:30
213 changed files with 29009 additions and 49738 deletions

View File

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

View File

@@ -286,99 +286,6 @@
"search_index": 0,
"set_only_once": 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,
@@ -392,7 +299,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2017-09-05 10:10:03.117505",
"modified": "2017-06-16 17:39:50.614522",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@@ -5,20 +5,10 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import cint
from frappe import _
from frappe.utils import cint, comma_and
from frappe.model.document import Document
class AccountsSettings(Document):
def on_update(self):
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)
pass

View File

@@ -1,35 +0,0 @@
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);
}
};

View File

@@ -1,22 +0,0 @@
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)

View File

@@ -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
class JournalEntry(AccountsController):
def __init__(self, *args, **kwargs):
super(JournalEntry, self).__init__(*args, **kwargs)
def __init__(self, arg1, arg2=None):
super(JournalEntry, self).__init__(arg1, arg2)
def get_feed(self):
return self.voucher_type

View File

@@ -403,13 +403,6 @@ frappe.ui.form.on('Payment Entry', {
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) {
@@ -428,13 +421,6 @@ frappe.ui.form.on('Payment Entry', {
frm.events.set_difference_amount(frm);
}
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) {

View File

@@ -765,7 +765,6 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
pe.append("references", {
"reference_doctype": dt,
"reference_name": dn,
"bill_no": doc.get("bill_no"),
"due_date": doc.get("due_date"),
"total_amount": grand_total,
"outstanding_amount": outstanding_amount,

View File

@@ -296,7 +296,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-10-16 17:37:01.192312",
"modified": "2017-09-04 17:37:01.192312",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry Reference",
@@ -311,4 +311,4 @@
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}
}

View File

@@ -30,8 +30,7 @@ class PaymentReconciliation(Document):
return payment_entries
def get_jv_entries(self):
dr_or_cr = "credit_in_account_currency" if self.party_type == "Customer" \
else "debit_in_account_currency"
dr_or_cr = self.get_dr_or_cr()
bank_account_condition = "t2.against_account like %(bank_cash_account)s" \
if self.bank_cash_account else "1=1"
@@ -73,13 +72,13 @@ class PaymentReconciliation(Document):
row = self.append('payments', {})
row.update(e)
def get_invoice_entries(self):
def get_invoice_entries(self, paying_party=False):
#Fetch JVs, Sales and Purchase Invoices for 'invoices' to reconcile against
condition = self.check_condition()
non_reconciled_invoices = get_outstanding_invoices(self.party_type, self.party,
self.receivable_payable_account, condition=condition)
self.receivable_payable_account, condition=condition, paying_party=paying_party)
self.add_invoice_entries(non_reconciled_invoices)
@@ -103,8 +102,7 @@ class PaymentReconciliation(Document):
self.get_invoice_entries()
self.validate_invoice()
dr_or_cr = "credit_in_account_currency" \
if self.party_type == "Customer" else "debit_in_account_currency"
dr_or_cr = self.get_dr_or_cr()
lst = []
for e in self.get('payments'):
@@ -184,3 +182,12 @@ class PaymentReconciliation(Document):
cond += " and `{0}` <= {1}".format(dr_or_cr, flt(self.maximum_amount))
return cond
def get_dr_or_cr(self):
'''Return credit_in_account_currency if not set and party is customer.'''
if hasattr(self, "dr_or_cr"):
return self.dr_or_cr
if self.party_type == 'Customer':
return "credit_in_account_currency"
else:
return "debit_in_account_currency"

View File

@@ -37,10 +37,10 @@ frappe.ui.form.on('POS Profile', {
return { filters: { doc_type: "Sales Invoice", print_format_type: "Js"} };
});
frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'use_pos_in_offline_mode', (r) => {
is_offline = r && cint(r.use_pos_in_offline_mode)
frm.toggle_display('offline_pos_section', is_offline);
frm.toggle_display('print_format_for_online', !is_offline);
frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'is_online', (r) => {
is_online = r && cint(r.is_online)
frm.toggle_display('offline_pos_section', !is_online);
frm.toggle_display('print_format_for_online', is_online);
});
},

View File

@@ -3,17 +3,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class POSSettings(Document):
def validate(self):
self.set_link_for_pos()
def set_link_for_pos(self):
link = 'pos' if self.use_pos_in_offline_mode else 'point-of-sale'
desktop_icon = frappe.db.get_value('Desktop Icon',
{'standard': 1, 'module_name': 'POS'}, 'name')
if desktop_icon:
frappe.db.set_value('Desktop Icon', desktop_icon, 'link', link)
pass

View File

@@ -1023,7 +1023,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"precision": "2",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -1284,7 +1284,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-09-27 08:31:38.432574",
"modified": "2017-08-31 16:34:41.614743",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Pricing Rule",

View File

@@ -348,6 +348,7 @@ def apply_internal_priority(pricing_rules, field_set, args):
return filtered_rules or pricing_rules
def set_transaction_type(args):
if args.transaction_type:return
if args.doctype in ("Opportunity", "Quotation", "Sales Order", "Delivery Note", "Sales Invoice"):
args.transaction_type = "selling"
elif args.doctype in ("Material Request", "Supplier Quotation", "Purchase Order",

View File

@@ -22,8 +22,8 @@ form_grid_templates = {
}
class PurchaseInvoice(BuyingController):
def __init__(self, *args, **kwargs):
super(PurchaseInvoice, self).__init__(*args, **kwargs)
def __init__(self, arg1, arg2=None):
super(PurchaseInvoice, self).__init__(arg1, arg2)
self.status_updater = [{
'source_dt': 'Purchase Invoice Item',
'target_dt': 'Purchase Order Item',

View File

@@ -88,7 +88,7 @@ def update_pos_profile_data(doc, pos_profile, company_data):
doc.naming_series = pos_profile.get('naming_series') or 'SINV-'
doc.letter_head = pos_profile.get('letter_head') or company_data.default_letter_head
doc.ignore_pricing_rule = pos_profile.get('ignore_pricing_rule') or 0
doc.apply_discount_on = pos_profile.get('apply_discount_on') or ''
doc.apply_discount_on = pos_profile.get('apply_discount_on') if pos_profile.get('apply_discount') else ''
doc.customer_group = pos_profile.get('customer_group') or get_root('Customer Group')
doc.territory = pos_profile.get('territory') or get_root('Territory')
doc.terms = frappe.db.get_value('Terms and Conditions', pos_profile.get('tc_name'), 'terms') or doc.terms or ''
@@ -417,7 +417,6 @@ def make_contact(args,customer):
'link_doctype': 'Customer',
'link_name': customer
})
doc.flags.ignore_mandatory = True
doc.save(ignore_permissions=True)
def make_address(args, customer):
@@ -442,7 +441,6 @@ def make_address(args, customer):
address.is_primary_address = 1
address.is_shipping_address = 1
address.update(args)
address.flags.ignore_mandatory = True
address.save(ignore_permissions = True)
def make_email_queue(email_queue):
@@ -486,21 +484,17 @@ def submit_invoice(si_doc, name, doc, name_list):
if frappe.message_log: frappe.message_log.pop()
frappe.db.rollback()
frappe.log_error(frappe.get_traceback())
name_list = save_invoice(doc, name, name_list)
name_list = save_invoice(e, si_doc, name, name_list)
return name_list
def save_invoice(doc, name, name_list):
def save_invoice(e, si_doc, name, name_list):
try:
if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}):
si = frappe.new_doc('Sales Invoice')
si.update(doc)
si.set_posting_time = 1
si.customer = get_customer_id(doc)
si.due_date = doc.get('posting_date')
si.flags.ignore_mandatory = True
si.insert(ignore_permissions=True)
frappe.db.commit()
si_doc.docstatus = 0
si_doc.flags.ignore_mandatory = True
si_doc.due_date = si_doc.posting_date
si_doc.insert()
name_list.append(name)
except Exception:
frappe.log_error(frappe.get_traceback())

View File

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

View File

@@ -27,8 +27,8 @@ form_grid_templates = {
}
class SalesInvoice(SellingController):
def __init__(self, *args, **kwargs):
super(SalesInvoice, self).__init__(*args, **kwargs)
def __init__(self, arg1, arg2=None):
super(SalesInvoice, self).__init__(arg1, arg2)
self.status_updater = [{
'source_dt': 'Sales Invoice Item',
'target_field': 'billed_amt',

View File

@@ -3,8 +3,8 @@
from __future__ import unicode_literals
import frappe
import unittest, copy, time
from frappe.utils import nowdate, add_days, flt, cint
import unittest, copy
from frappe.utils import nowdate, add_days, flt
from frappe.model.dynamic_links import get_dynamic_link_map
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
@@ -665,47 +665,6 @@ class TestSalesInvoice(unittest.TestCase):
self.pos_gl_entry(si, pos, 330)
def test_make_pos_invoice_in_draft(self):
from erpnext.accounts.doctype.sales_invoice.pos import make_invoice
from erpnext.stock.doctype.item.test_item import make_item
set_perpetual_inventory()
allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
if allow_negative_stock:
frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0)
make_pos_profile()
timestamp = cint(time.time())
item = make_item("_Test POS Item")
pos = copy.deepcopy(test_records[1])
pos['items'][0]['item_code'] = item.name
pos["is_pos"] = 1
pos["offline_pos_name"] = timestamp
pos["update_stock"] = 1
pos["payments"] = [{'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 300},
{'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 330}]
invoice_data = [{timestamp: pos}]
si = make_invoice(invoice_data).get('invoice')
self.assertEquals(si[0], timestamp)
sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp})
self.assertEquals(sales_invoice[0].docstatus, 0)
timestamp = cint(time.time())
pos["offline_pos_name"] = timestamp
invoice_data = [{timestamp: pos}]
si1 = make_invoice(invoice_data).get('invoice')
self.assertEquals(si1[0], timestamp)
sales_invoice1 = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp})
self.assertEquals(sales_invoice1[0].docstatus, 0)
if allow_negative_stock:
frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 1)
def pos_gl_entry(self, si, pos, cash_amount):
# check stock ledger entries
sle = frappe.db.sql("""select * from `tabStock Ledger Entry`
@@ -1125,7 +1084,7 @@ class TestSalesInvoice(unittest.TestCase):
si.items[0].price_list_rate = price_list_rate
si.items[0].margin_type = 'Percentage'
si.items[0].margin_rate_or_amount = 25
si.save()
si.insert()
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):

View File

@@ -699,7 +699,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"precision": "2",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -2166,7 +2166,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-09-27 08:31:37.827893",
"modified": "2017-07-17 17:54:48.246507",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",

View File

@@ -3,12 +3,6 @@
frappe.ui.form.on('Subscription', {
setup: function(frm) {
frm.fields_dict['reference_doctype'].get_query = function(doc) {
return {
query: "erpnext.accounts.doctype.subscription.subscription.subscription_doctype_query"
};
};
frm.fields_dict['reference_document'].get_query = function() {
return {
filters: {

View File

@@ -135,6 +135,66 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "disabled",
"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": "Disabled",
"length": 0,
"no_copy": 1,
"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": "submit_on_creation",
"fieldtype": "Check",
"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": "Submit on Creation",
"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,
@@ -226,12 +286,12 @@
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "submit_on_creation",
"fieldtype": "Check",
"fieldname": "next_schedule_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -239,44 +299,14 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Submit on Creation",
"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": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "disabled",
"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": "Disabled",
"label": "Next Schedule Date",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
@@ -290,7 +320,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_10",
"fieldname": "frequency_detail",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -299,95 +329,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"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": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "from_date",
"fieldtype": "Date",
"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": "From Date",
"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": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "to_date",
"fieldtype": "Date",
"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": "To Date",
"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": "column_break_13",
"fieldtype": "Column 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": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -433,6 +375,35 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_12",
"fieldtype": "Column 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,
"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": 1,
@@ -464,41 +435,11 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "next_schedule_date",
"fieldtype": "Date",
"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": "Next Schedule Date",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"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,
"collapsible": 0,
"columns": 0,
"fieldname": "notification",
"fieldtype": "Section Break",
@@ -554,38 +495,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval: doc.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_on_submit": 0,
@@ -684,69 +593,6 @@
"bold": 0,
"collapsible": 1,
"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",
"fieldtype": "Section Break",
"hidden": 0,
@@ -844,7 +690,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-10-10 17:28:10.105561",
"modified": "2017-09-14 12:09:38.471458",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Subscription",
@@ -854,7 +700,7 @@
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 1,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
@@ -874,7 +720,7 @@
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 1,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
@@ -894,7 +740,7 @@
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 1,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,

View File

@@ -7,10 +7,9 @@ import frappe
import calendar
from frappe import _
from frappe.desk.form import assign_to
from frappe.utils.jinja import validate_template
from dateutil.relativedelta import relativedelta
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
from frappe.model.document import Document
month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12}
@@ -21,30 +20,16 @@ class Subscription(Document):
self.validate_next_schedule_date()
self.validate_email_id()
validate_template(self.subject or "")
validate_template(self.message or "")
def before_submit(self):
self.set_next_schedule_date()
def on_submit(self):
# self.update_subscription_id()
self.update_subscription_data()
self.update_subscription_id()
def on_update_after_submit(self):
self.update_subscription_data()
self.validate_dates()
self.set_next_schedule_date()
def before_cancel(self):
self.unlink_subscription_id()
def unlink_subscription_id(self):
doc = frappe.get_doc(self.reference_doctype, self.reference_document)
if doc.meta.get_field('subscription'):
doc.subscription = None
doc.db_update()
def validate_dates(self):
if self.end_date and getdate(self.start_date) > getdate(self.end_date):
frappe.throw(_("End date must be greater than start date"))
@@ -79,21 +64,6 @@ class Subscription(Document):
self.next_schedule_date = get_next_schedule_date(self.start_date,
self.frequency, self.repeat_on_day)
def update_subscription_data(self):
update_doc = False
doc = frappe.get_doc(self.reference_doctype, self.reference_document)
if frappe.get_meta(self.reference_doctype).get_field("from_date"):
doc.from_date = self.from_date
doc.to_date = self.to_date
update_doc = True
if not doc.subscription:
doc.subscription = self.name
update_doc = True
if update_doc:
doc.db_update()
def update_subscription_id(self):
doc = frappe.get_doc(self.reference_doctype, self.reference_document)
if not doc.meta.get_field('subscription'):
@@ -142,31 +112,21 @@ def get_subscription_entries(date):
def create_documents(data, schedule_date):
try:
doc = make_new_document(data, schedule_date)
if getattr(doc, "from_date", None):
update_subscription_period(data, doc)
if data.notify_by_email and data.recipients:
print_format = data.print_format or "Standard"
send_notification(doc, data, print_format=print_format)
send_notification(doc, print_format, data.recipients)
frappe.db.commit()
except Exception:
frappe.db.rollback()
frappe.db.begin()
frappe.log_error(frappe.get_traceback())
disable_subscription(data)
disabled_subscription(data)
frappe.db.commit()
if data.reference_document and not frappe.flags.in_test:
notify_error_to_user(data)
def update_subscription_period(data, doc):
from_date = doc.from_date
to_date = doc.to_date
frappe.db.set_value('Subscription', data.name, 'from_date', from_date)
frappe.db.set_value('Subscription', data.name, 'to_date', to_date)
def disable_subscription(data):
def disabled_subscription(data):
subscription = frappe.get_doc('Subscription', data.name)
subscription.db_set('disabled', 1)
@@ -200,35 +160,9 @@ def update_doc(new_document, reference_doc, args, schedule_date):
if new_document.meta.get_field('set_posting_time'):
new_document.set('set_posting_time', 1)
mcount = month_map.get(args.frequency)
if new_document.meta.get_field('subscription'):
new_document.set('subscription', args.name)
for fieldname in ['naming_series', 'ignore_pricing_rule', 'posting_time'
'select_print_heading', 'remarks', 'owner']:
if new_document.meta.get_field(fieldname):
new_document.set(fieldname, reference_doc.get(fieldname))
# copy item fields
if new_document.meta.get_field('items'):
for i, item in enumerate(new_document.items):
for fieldname in ("page_break",):
item.set(fieldname, reference_doc.items[i].get(fieldname))
if args.from_date and args.to_date:
from_date = get_next_date(args.from_date, mcount)
if (cstr(get_first_day(args.from_date)) == cstr(args.from_date)) and \
(cstr(get_last_day(args.to_date)) == cstr(args.to_date)):
to_date = get_last_day(get_next_date(args.to_date, mcount))
else:
to_date = get_next_date(args.to_date, mcount)
if new_document.meta.get_field('from_date'):
new_document.set('from_date', from_date)
new_document.set('to_date', to_date)
new_document.run_method("on_recurring", reference_doc=reference_doc, subscription_doc=args)
for data in new_document.meta.fields:
if data.fieldtype == 'Date' and data.reqd:
@@ -240,25 +174,14 @@ def get_next_date(dt, mcount, day=None):
return dt
def send_notification(new_rv, subscription_doc, print_format='Standard'):
def send_notification(new_rv, print_format='Standard', recipients=None):
"""Notify concerned persons about recurring document generation"""
print_format = print_format
if not subscription_doc.subject:
subject = _("New {0}: #{1}").format(new_rv.doctype, new_rv.name)
elif "{" in subscription_doc.subject:
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)
frappe.sendmail(recipients,
subject= _("New {0}: #{1}").format(new_rv.doctype, new_rv.name),
message = _("Please find attached {0} #{1}").format(new_rv.doctype, new_rv.name),
attachments = [frappe.attach_print(new_rv.doctype, new_rv.name, file_name=new_rv.name, print_format=print_format)])
def notify_errors(doc, doctype, party, owner, name):
recipients = get_system_managers(only_name=True)
@@ -302,20 +225,4 @@ def stop_resume_subscription(subscription, status):
doc.update_status(status)
doc.save()
return doc.status
def subscription_doctype_query(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select parent from `tabDocField`
where fieldname = 'subscription'
and parent like %(txt)s
order by
if(locate(%(_txt)s, parent), locate(%(_txt)s, parent), 99999),
parent
limit %(start)s, %(page_len)s""".format(**{
'key': searchfield,
}), {
'txt': "%%%s%%" % txt,
'_txt': txt.replace("%", ""),
'start': start,
'page_len': page_len
})
return doc.status

View File

@@ -8,7 +8,6 @@ from frappe import _
from frappe.model.document import Document
from frappe.utils import cstr, cint
from frappe.contacts.doctype.address.address import get_default_address
from frappe.utils.nestedset import get_root_of
from erpnext.setup.doctype.customer_group.customer_group import get_parent_customer_groups
class IncorrectCustomerGroup(frappe.ValidationError): pass
@@ -137,7 +136,7 @@ def get_tax_template(posting_date, args):
if key=="use_for_shopping_cart":
conditions.append("use_for_shopping_cart = {0}".format(1 if value else 0))
if key == 'customer_group':
if not value: value = get_root_of("Customer Group")
if not value: value = _("All Customer Groups")
customer_group_condition = get_customer_group_condition(value)
conditions.append("ifnull({0}, '') in ('', {1})".format(key, customer_group_condition))
else:

View File

@@ -113,7 +113,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
});
this.page.add_menu_item(__("Sync Offline Invoices"), function () {
me.freeze_screen = true;
me.sync_sales_invoice()
});
@@ -1685,7 +1684,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
set_interval_for_si_sync: function () {
var me = this;
setInterval(function () {
me.freeze_screen = false;
me.sync_sales_invoice()
}, 60000)
},
@@ -1699,12 +1697,9 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.freeze = this.customer_doc.display
}
freeze_screen = this.freeze_screen || false;
if ((this.si_docs.length || this.email_queue_list || this.customers_list) && !this.freeze) {
frappe.call({
method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice",
freeze: freeze_screen,
args: {
doc_list: me.si_docs,
email_queue_list: me.email_queue_list,

View File

@@ -68,8 +68,7 @@ def set_address_details(out, party, party_type, doctype=None, company=None):
billing_address_field = "customer_address" if party_type == "Lead" \
else party_type.lower() + "_address"
out[billing_address_field] = get_default_address(party_type, party.name)
if doctype:
out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field]))
out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field]))
# address display
out.address_display = get_address_display(out[billing_address_field])
@@ -78,8 +77,7 @@ def set_address_details(out, party, party_type, doctype=None, company=None):
if party_type in ["Customer", "Lead"]:
out.shipping_address_name = get_default_address(party_type, party.name, 'is_shipping_address')
out.shipping_address = get_address_display(out["shipping_address_name"])
if doctype:
out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name))
out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name))
if doctype and doctype in ['Delivery Note', 'Sales Invoice']:
out.update(get_company_address(company))
@@ -322,15 +320,11 @@ def set_taxes(party, party_type, posting_date, company, customer_group=None, sup
from erpnext.accounts.doctype.tax_rule.tax_rule import get_tax_template, get_party_details
args = {
party_type.lower(): party,
"customer_group": customer_group,
"supplier_type": supplier_type,
"company": company
}
if customer_group:
args['customer_group'] = customer_group
if supplier_type:
args['supplier_type'] = supplier_type
if billing_address or shipping_address:
args.update(get_party_details(party, party_type, {"billing_address": billing_address, \
"shipping_address": shipping_address }))

View File

@@ -83,7 +83,7 @@ frappe.query_reports["General Ledger"] = {
return;
}
var fieldname = party_type.toLowerCase() + "_name";
var fieldname = frappe.model.scrub(party_type)+"_name";
frappe.db.get_value(party_type, party, fieldname, function(value) {
frappe.query_report_filters_by_name.party_name.set_value(value[fieldname]);
});

View File

@@ -107,8 +107,6 @@ class GrossProfitGenerator(object):
def process(self):
self.grouped = {}
self.grouped_data = []
for row in self.si_list:
if self.skip_row(row, self.product_bundles):
continue
@@ -152,6 +150,7 @@ class GrossProfitGenerator(object):
def get_average_rate_based_on_group_by(self):
# sum buying / selling totals for group
self.grouped_data = []
for key in self.grouped.keys():
if self.filters.get("group_by") != "Invoice":
for i, row in enumerate(self.grouped[key]):

View File

@@ -569,11 +569,11 @@ def get_stock_rbnb_difference(posting_date, company):
# Amount should be credited
return flt(stock_rbnb) + flt(sys_bal)
def get_outstanding_invoices(party_type, party, account, condition=None):
def get_outstanding_invoices(party_type, party, account, condition=None, paying_party=False):
outstanding_invoices = []
precision = frappe.get_precision("Sales Invoice", "outstanding_amount")
if party_type=="Customer":
if party_type=="Customer" or paying_party:
dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
payment_dr_or_cr = "payment_gl_entry.credit_in_account_currency - payment_gl_entry.debit_in_account_currency"
else:

View File

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

View File

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

View File

@@ -20,8 +20,8 @@ form_grid_templates = {
}
class PurchaseOrder(BuyingController):
def __init__(self, *args, **kwargs):
super(PurchaseOrder, self).__init__(*args, **kwargs)
def __init__(self, arg1, arg2=None):
super(PurchaseOrder, self).__init__(arg1, arg2)
self.status_updater = [{
'source_dt': 'Purchase Order Item',
'target_dt': 'Material Request Item',
@@ -116,13 +116,14 @@ class PurchaseOrder(BuyingController):
d.discount_percentage = last_purchase_details['discount_percentage']
d.base_rate = last_purchase_details['base_rate'] * (flt(d.conversion_factor) or 1.0)
d.price_list_rate = d.base_price_list_rate / conversion_rate
d.last_purchase_rate = d.base_rate / conversion_rate
d.rate = d.base_rate / conversion_rate
else:
msgprint(_("Last purchase rate not found"))
item_last_purchase_rate = frappe.db.get_value("Item", d.item_code, "last_purchase_rate")
if item_last_purchase_rate:
d.base_price_list_rate = d.base_rate = d.price_list_rate \
= d.last_purchase_rate = item_last_purchase_rate
= d.rate = item_last_purchase_rate
# Check for Closed status
def check_for_closed_status(self):

View File

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

View File

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

View File

@@ -254,21 +254,6 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
}
})
}, __("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
this.frm.add_custom_button(__('Possible Supplier'), function() {
// Create a dialog window for the user to pick their supplier

View File

@@ -816,7 +816,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-10-17 17:27:06.281494",
"modified": "2017-07-21 14:06:46.309322",
"modified_by": "Administrator",
"module": "Buying",
"name": "Request for Quotation",
@@ -903,6 +903,26 @@
"submit": 0,
"write": 0
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Supplier",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 0
},
{
"amend": 0,
"apply_user_permissions": 0,

View File

@@ -2,29 +2,29 @@
// For license information, please see license.txt
frappe.query_reports["Quoted Item Comparison"] = {
filters: [
"filters": [
{
fieldtype: "Link",
label: __("Supplier Quotation"),
options: "Supplier Quotation",
fieldname: "supplier_quotation",
default: "",
get_query: () => {
"fieldname": "supplier_quotation",
"label": __("Supplier Quotation"),
"fieldtype": "Link",
"options": "Supplier Quotation",
"default": "",
"get_query": function () {
return { filters: { "docstatus": ["<", 2] } }
}
},
{
reqd: 1,
default: "",
options: "Item",
label: __("Item"),
fieldname: "item",
fieldtype: "Link",
get_query: () => {
let quote = frappe.query_report_filters_by_name.supplier_quotation.get_value();
"fieldname": "item",
"label": __("Item"),
"fieldtype": "Link",
"options": "Item",
"default": "",
"reqd": 1,
"get_query": function () {
var quote = frappe.query_report_filters_by_name.supplier_quotation.get_value();
if (quote != "") {
return {
query: "erpnext.stock.doctype.quality_inspection.quality_inspection.item_query",
query: "erpnext.buying.doctype.quality_inspection.quality_inspection.item_query",
filters: {
"from": "Supplier Quotation Item",
"parent": quote
@@ -39,50 +39,47 @@ frappe.query_reports["Quoted Item Comparison"] = {
}
}
],
onload: (report) => {
onload: function (report) {
// Create a button for setting the default supplier
report.page.add_inner_button(__("Select Default Supplier"), () => {
let reporter = frappe.query_reports["Quoted Item Comparison"];
report.page.add_inner_button(__("Select Default Supplier"), function () {
var reporter = frappe.query_reports["Quoted Item Comparison"];
//Always make a new one so that the latest values get updated
reporter.make_default_supplier_dialog(report);
report.dialog.show();
setTimeout(function () { report.dialog.input.focus(); }, 1000);
}, 'Tools');
},
make_default_supplier_dialog: (report) => {
"make_default_supplier_dialog": function (report) {
// Get the name of the item to change
if(!report.data) return;
let filters = report.get_values();
let item_code = filters.item;
var filters = report.get_values();
var item_code = filters.item;
// Get a list of the suppliers (with a blank as well) for the user to select
let suppliers = $.map(report.data, (row, idx)=>{ return row.supplier_name })
var select_options = "";
for (let supplier of report.data) {
select_options += supplier.supplier_name + '\n'
}
// Create a dialog window for the user to pick their supplier
let dialog = new frappe.ui.Dialog({
var d = new frappe.ui.Dialog({
title: __('Select Default Supplier'),
fields: [
{
reqd: 1,
label: 'Supplier',
fieldtype: 'Link',
options: 'Supplier',
fieldname: 'supplier',
get_query: () => {
return {
filters: {
'name': ['in', suppliers]
}
}
}
}
{ fieldname: 'supplier', fieldtype: 'Select', label: 'Supplier', reqd: 1, options: select_options },
{ fieldname: 'ok_button', fieldtype: 'Button', label: 'Set Default Supplier' },
]
});
dialog.set_primary_action("Set Default Supplier", () => {
let values = dialog.get_values();
if(values) {
// On the user clicking the ok button
d.fields_dict.ok_button.input.onclick = function () {
var btn = d.fields_dict.ok_button.input;
var v = report.dialog.get_values();
if (v) {
$(btn).set_working();
// Set the default_supplier field of the appropriate Item to the selected supplier
frappe.call({
method: "frappe.client.set_value",
@@ -90,17 +87,17 @@ frappe.query_reports["Quoted Item Comparison"] = {
doctype: "Item",
name: item_code,
fieldname: "default_supplier",
value: values.supplier,
value: v.supplier,
},
freeze: true,
callback: (r) => {
callback: function (r) {
$(btn).done_working();
frappe.msgprint("Successfully Set Supplier");
dialog.hide();
report.dialog.hide();
}
});
}
});
dialog.show();
}
report.dialog = d;
}
}

View File

@@ -8,55 +8,53 @@ import frappe
def execute(filters=None):
qty_list = get_quantity_list(filters.item)
data = get_quote_list(filters.item, qty_list)
columns = get_columns(qty_list)
return columns, data
def get_quote_list(item, qty_list):
out = []
if not item:
return []
suppliers = []
price_data = []
company_currency = frappe.db.get_default("currency")
float_precision = cint(frappe.db.get_default("float_precision")) or 2
# Get the list of suppliers
for root in frappe.db.sql("""select parent, qty, rate from `tabSupplier Quotation Item`
where item_code=%s and docstatus < 2""", item, as_dict=1):
for splr in frappe.db.sql("""select supplier from `tabSupplier Quotation`
where name =%s and docstatus < 2""", root.parent, as_dict=1):
ip = frappe._dict({
if item:
price_data = []
suppliers = []
company_currency = frappe.db.get_default("currency")
float_precision = cint(frappe.db.get_default("float_precision")) or 2
# Get the list of suppliers
for root in frappe.db.sql("""select parent, qty, rate from `tabSupplier Quotation Item` where item_code=%s and docstatus < 2""", item, as_dict=1):
for splr in frappe.db.sql("""SELECT supplier from `tabSupplier Quotation` where name =%s and docstatus < 2""", root.parent, as_dict=1):
ip = frappe._dict({
"supplier": splr.supplier,
"qty": root.qty,
"parent": root.parent,
"rate": root.rate
"rate": root.rate})
price_data.append(ip)
suppliers.append(splr.supplier)
#Add a row for each supplier
for root in set(suppliers):
supplier_currency = frappe.db.get_value("Supplier", root, "default_currency")
if supplier_currency:
exchange_rate = get_exchange_rate(supplier_currency, company_currency)
else:
exchange_rate = 1
row = frappe._dict({
"supplier_name": root
})
price_data.append(ip)
suppliers.append(splr.supplier)
#Add a row for each supplier
for root in set(suppliers):
supplier_currency = frappe.db.get_value("Supplier", root, "default_currency")
if supplier_currency:
exchange_rate = get_exchange_rate(supplier_currency, company_currency)
else:
exchange_rate = 1
row = frappe._dict({
"supplier_name": root
})
for col in qty_list:
# Get the quantity for this row
for item_price in price_data:
if str(item_price.qty) == col.key and item_price.supplier == root:
row[col.key] = flt(item_price.rate * exchange_rate, float_precision)
row[col.key + "QUOTE"] = item_price.parent
break
else:
row[col.key] = ""
row[col.key + "QUOTE"] = ""
out.append(row)
for col in qty_list:
# Get the quantity for this row
for item_price in price_data:
if str(item_price.qty) == col.key and item_price.supplier == root:
row[col.key] = flt(item_price.rate * exchange_rate, float_precision)
row[col.key + "QUOTE"] = item_price.parent
break
else:
row[col.key] = ""
row[col.key + "QUOTE"] = ""
out.append(row)
return out
@@ -64,8 +62,7 @@ def get_quantity_list(item):
out = []
if item:
qty_list = frappe.db.sql("""select distinct qty from `tabSupplier Quotation Item`
where ifnull(item_code,'')=%s and docstatus < 2""", item, as_dict=1)
qty_list = frappe.db.sql("""select distinct qty from `tabSupplier Quotation Item` where ifnull(item_code,'')=%s and docstatus < 2""", item, as_dict=1)
qty_list.sort(reverse=False)
for qt in qty_list:
col = frappe._dict({
@@ -101,4 +98,4 @@ def get_columns(qty_list):
"width": 90
})
return columns
return columns

View File

@@ -1,4 +1,4 @@
- POS - Online & Offline
#### POS
- Now user has an option to enable or disable Offline POS mode from POS Settings
- Provision to select the Item's serial number from the dropdown while adding item in the cart
- Indicator for stock availability in Online POS Mode.
@@ -6,25 +6,3 @@
#### Subscription
- Setup recurring documents using **Subscription**
- User can schedule the subscription for doctypes other than Sales Invoice, Purchase Invoice etc.
#### Healthcare Domain
- Clinic / Practice Management
- Patient
- Physician, Physician scheduling
- Appointment
- Vital Signs
- Consultation
- Medical Code Standards
- Patient Medical Record
- Laboratory
- Sample Collection
- Lab Test
- Patient Portal
#### School Fees Management
- Fee Structure
- Fee Schedule
- Payment against Fees
#### Setup Wizard
- Broken into 2 parts with a fresh looks

View File

@@ -315,16 +315,11 @@ def get_data():
"name": "Payment Gateway Account",
"description": _("Setup Gateway accounts.")
},
{
"type": "doctype",
"name": "POS Settings",
"description": _("Setup mode of POS (Online / Offline)")
},
{
"type": "doctype",
"name": "POS Profile",
"label": _("Point-of-Sale Profile"),
"description": _("Setup default values for POS Invoices")
"description": _("Rules to calculate shipping amount for a sale")
},
{
"type": "doctype",

View File

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

View File

@@ -123,12 +123,6 @@ def get_data():
"is_query_report": True,
"name": "BOM Search",
"doctype": "BOM"
},
{
"type": "report",
"is_query_report": True,
"name": "BOM Stock Report",
"doctype": "BOM"
}
]
},

View File

@@ -105,11 +105,6 @@ def get_data():
"name": "Pricing Rule",
"description": _("Rules for applying pricing and discount.")
},
{
"type": "doctype",
"name": "Item Variant Settings",
"description": _("Item Variant Settings."),
},
]
},

View File

@@ -15,8 +15,8 @@ from erpnext.exceptions import InvalidCurrency
force_item_fields = ("item_group", "barcode", "brand", "stock_uom")
class AccountsController(TransactionBase):
def __init__(self, *args, **kwargs):
super(AccountsController, self).__init__(*args, **kwargs)
def __init__(self, arg1, arg2=None):
super(AccountsController, self).__init__(arg1, arg2)
@property
def company_currency(self):
@@ -187,6 +187,9 @@ class AccountsController(TransactionBase):
if stock_qty != len(get_serial_nos(item.get('serial_no'))):
item.set(fieldname, value)
elif fieldname == "conversion_factor" and not item.get("conversion_factor"):
item.set(fieldname, value)
if ret.get("pricing_rule"):
# if user changed the discount percentage then set user's discount percentage ?
item.set("discount_percentage", ret.get("discount_percentage"))

View File

@@ -61,7 +61,7 @@ class BuyingController(StockController):
# set contact and address details for supplier, if they are not mentioned
if getattr(self, "supplier", None):
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.update_if_missing(get_party_details(self.supplier, party_type="Supplier", ignore_permissions=self.flags.ignore_permissions))
self.set_missing_item_details(for_validate)

View File

@@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import cstr, flt
import json, copy
import json
class ItemVariantExistsError(frappe.ValidationError): pass
class InvalidItemAttributeValueError(frappe.ValidationError): pass
@@ -174,30 +174,18 @@ def copy_attributes_to_variant(item, variant):
# copy non no-copy fields
exclude_fields = ["naming_series", "item_code", "item_name", "show_in_website",
"show_variant_in_website", "opening_stock", "variant_of", "valuation_rate"]
exclude_fields = ["item_code", "item_name", "show_in_website"]
if item.variant_based_on=='Manufacturer':
# don't copy manufacturer values if based on 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:
# "Table" is part of `no_value_field` but we shouldn't ignore tables
if (field.reqd or field.fieldname in allow_fields) and field.fieldname not in exclude_fields:
if (field.fieldtype == 'Table' or field.fieldtype not in no_value_fields) \
and (not field.no_copy) and field.fieldname not in exclude_fields:
if variant.get(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.set(field.fieldname, item.get(field.fieldname))
variant.variant_of = item.name
variant.has_variants = 0
if not variant.description:
@@ -205,10 +193,9 @@ def copy_attributes_to_variant(item, variant):
if item.variant_based_on=='Item Attribute':
if variant.attributes:
if not variant.description:
variant.description += "\n"
for d in variant.attributes:
variant.description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"
variant.description += "\n"
for d in variant.attributes:
variant.description += "<p>" + d.attribute + ": " + cstr(d.attribute_value) + "</p>"
def make_variant_item_code(template_item_code, template_item_name, variant):
"""Uses template's item code and abbreviations to make variant's item code"""

View File

@@ -49,8 +49,7 @@ class SellingController(StockController):
if getattr(self, "customer", None):
from erpnext.accounts.party import _get_party_details
party_details = _get_party_details(self.customer,
ignore_permissions=self.flags.ignore_permissions,
doctype=self.doctype, company=self.company)
ignore_permissions=self.flags.ignore_permissions)
if not self.meta.get_field("sales_team"):
party_details.pop("sales_team")

View File

@@ -4,7 +4,6 @@ import frappe
import json
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
# python 3 compatibility stuff
@@ -55,7 +54,5 @@ def make_item_variant():
class TestItemVariant(unittest.TestCase):
def test_tables_in_template_copied_to_variant(self):
fields = [{'field_name': 'quality_parameters'}]
set_item_variant_settings(fields)
variant = make_item_variant()
self.assertNotEqual(variant.get("quality_parameters"), [])

View File

@@ -42,28 +42,10 @@ class Opportunity(TransactionBase):
if not self.with_items:
self.items = []
def make_new_lead_if_required(self):
"""Set lead against new opportunity"""
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})
if not lead_name:
sender_name = get_fullname(self.contact_email)
@@ -263,27 +245,6 @@ def make_quotation(source_name, target_doc=None):
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()
def make_supplier_quotation(source_name, target_doc=None):
doclist = get_mapped_doc("Opportunity", source_name, {
@@ -323,4 +284,4 @@ def auto_close_opportunity():
doc.status = "Closed"
doc.flags.ignore_permissions = True
doc.flags.ignore_mandatory = True
doc.save()
doc.save()

View File

@@ -4,7 +4,6 @@ from __future__ import unicode_literals
import frappe
from frappe.utils import today
from erpnext.crm.doctype.lead.lead import make_customer
from erpnext.crm.doctype.opportunity.opportunity import make_quotation
import unittest
@@ -26,45 +25,12 @@ class TestOpportunity(unittest.TestCase):
doc = frappe.get_doc('Opportunity', doc.name)
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):
args = frappe._dict(args)
opp_doc = frappe.get_doc({
"doctype": "Opportunity",
"enquiry_from": args.enquiry_from or "Customer",
"enquiry_from": "Customer" or args.enquiry_from,
"enquiry_type": "Sales",
"with_items": args.with_items or 0,
"transaction_date": today()

View File

@@ -278,16 +278,5 @@
"item_code": "Autocad",
"item_name": "Autocad",
"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"
}
]

View File

@@ -16,7 +16,6 @@ def setup(domain):
setup_user()
setup_employee()
setup_user_roles()
setup_role_permissions()
employees = frappe.get_all('Employee', fields=['name', 'date_of_joining'])
@@ -92,8 +91,7 @@ def setup_fiscal_year():
pass
# set the last fiscal year (current year) as default
if fiscal_year:
fiscal_year.set_as_default()
fiscal_year.set_as_default()
def setup_holiday_list():
"""Setup Holiday List for the current year"""
@@ -376,22 +374,6 @@ def setup_pos_profile():
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):
frappe.flags.in_import = True
data = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data',

View File

@@ -7,7 +7,6 @@ import frappe, random
from frappe.desk import query_report
from erpnext.stock.stock_ledger import NegativeStockError
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.purchase_receipt.purchase_receipt import make_purchase_return
@@ -60,7 +59,7 @@ def make_delivery_note():
try:
dn.submit()
frappe.db.commit()
except (NegativeStockError, SerialNoRequiredError, SerialNoQtyError, UnableToSelectBatchError):
except (NegativeStockError, SerialNoRequiredError, SerialNoQtyError):
frappe.db.rollback()
def make_stock_reconciliation():

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

View File

@@ -13,8 +13,4 @@
* 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}

View File

@@ -48,11 +48,4 @@ When you make a new Variant, the system will prompt you to select a Manufacturer
<img class='screenshot' alt='Setup Item Variant by Manufacturer'
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"
### 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'>
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"

View File

@@ -78,7 +78,7 @@ def create_invoice(company, patient, physician, consultation_id):
create_invoice_items(physician, sales_invoice, company)
sales_invoice.save(ignore_permissions=True)
frappe.db.sql("""update tabConsultation set invoice=%s where name=%s""", (sales_invoice.name, consultation_id))
frappe.db.sql(_("""update tabConsultation set invoice='{0}' where name='{1}'""").format(sales_invoice.name, consultation_id))
appointment = frappe.db.get_value("Consultation", consultation_id, "appointment")
if appointment:
frappe.db.set_value("Patient Appointment", appointment, "sales_invoice", sales_invoice.name)

View File

@@ -291,5 +291,5 @@ def create_invoice(company, patient, lab_tests, prescriptions):
@frappe.whitelist()
def get_lab_test_prescribed(patient):
return frappe.db.sql("""select cp.name, cp.test_code, cp.parent, cp.invoice, ct.physician, ct.consultation_date from tabConsultation ct,
`tabLab Prescription` cp where ct.patient=%s and cp.parent=ct.name and cp.test_created=0""", (patient))
return frappe.db.sql(_("""select cp.name, cp.test_code, cp.parent, cp.invoice, ct.physician, ct.consultation_date from tabConsultation ct,
`tabLab Prescription` cp where ct.patient='{0}' and cp.parent=ct.name and cp.test_created=0""").format(patient))

View File

@@ -111,10 +111,10 @@ def make_invoice(patient, company):
@frappe.whitelist()
def get_patient_detail(patient, company=None):
patient_dict = frappe.db.sql("""select * from tabPatient where name=%s""", (patient), as_dict=1)
patient_dict = frappe.db.sql(_("""select * from tabPatient where name='{0}'""").format(patient), as_dict=1)
if not patient_dict:
frappe.throw("Patient not found")
vital_sign = frappe.db.sql("""select * from `tabVital Signs` where patient=%s order by signs_date desc limit 1""", (patient), as_dict=1)
vital_sign = frappe.db.sql(_("""select * from `tabVital Signs` where patient='{0}' order by signs_date desc limit 1""").format(patient), as_dict=1)
details = patient_dict[0]
if vital_sign:

View File

@@ -183,20 +183,16 @@ var btn_create_vital_signs = function (frm) {
var btn_update_status = function(frm, status){
var doc = frm.doc;
frappe.confirm(__('Are you sure you want to cancel this appointment?'),
function() {
frappe.call({
method:
"erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_status",
args: {appointmentId: doc.name, status:status},
callback: function(data){
if(!data.exc){
frm.reload_doc();
}
}
});
frappe.call({
method:
"erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_status",
args: {appointmentId: doc.name, status:status},
callback: function(data){
if(!data.exc){
cur_frm.reload_doc();
}
}
);
});
};
var btn_invoice_consultation = function(frm){

View File

@@ -125,7 +125,7 @@ def create_invoice(company, physician, patient, appointment_id, appointment_date
create_invoice_items(appointment_id, physician, company, sales_invoice)
sales_invoice.save(ignore_permissions=True)
frappe.db.sql("""update `tabPatient Appointment` set sales_invoice=%s where name=%s""", (sales_invoice.name, appointment_id))
frappe.db.sql(_("""update `tabPatient Appointment` set sales_invoice='{0}' where name='{1}'""").format(sales_invoice.name, appointment_id))
frappe.db.set_value("Fee Validity", fee_validity.name, "ref_invoice", sales_invoice.name)
consultation = frappe.db.exists({
"doctype": "Consultation",

View File

@@ -11,7 +11,7 @@ app_email = "info@erpnext.com"
app_license = "GNU General Public License (v3)"
source_link = "https://github.com/frappe/erpnext"
develop_version = '9.x.x-develop'
develop_version = '8.x.x-beta'
error_report_email = "support@erpnext.com"
@@ -27,6 +27,7 @@ doctype_js = {
# setup wizard
setup_wizard_requires = "assets/erpnext/js/setup_wizard.js"
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"
before_install = "erpnext.setup.install.check_setup_wizard_not_completed"
@@ -47,7 +48,7 @@ treeviews = ['Account', 'Cost Center', 'Warehouse', 'Item Group', 'Customer Grou
update_website_context = "erpnext.shopping_cart.utils.update_website_context"
my_account_context = "erpnext.shopping_cart.utils.update_my_account_context"
email_append_to = ["Job Applicant", "Lead", "Opportunity", "Issue"]
email_append_to = ["Job Applicant", "Opportunity", "Issue"]
calendars = ["Task", "Production Order", "Leave Application", "Sales Order", "Holiday List"]
@@ -80,7 +81,7 @@ website_route_rules = [
{"from_route": "/supplier-quotations/<path:name>", "to_route": "order",
"defaults": {
"doctype": "Supplier Quotation",
"parents": [{"label": _("Supplier Quotation"), "route": "supplier-quotations"}]
"parents": [{"label": _("Supplier Quotation"), "route": "quotations"}]
}
},
{"from_route": "/quotations", "to_route": "Quotation"},

View File

@@ -149,7 +149,7 @@
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"in_standard_filter": 0,
"label": "Full Name",
"length": 0,
"no_copy": 0,
@@ -431,7 +431,7 @@
"no_copy": 0,
"oldfieldname": "gender",
"oldfieldtype": "Select",
"options": "Gender",
"options": "Gender",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@@ -2432,7 +2432,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-10-04 11:42:02.495731",
"modified": "2017-06-13 14:29:13.694009",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee",

View File

@@ -21,7 +21,7 @@ class JobOpening(WebsiteGenerator):
self.route = frappe.scrub(self.job_title).replace('_', '-')
def get_context(self, context):
context.parents = [{'route': 'jobs', 'title': _('All Jobs') }]
context.parents = [{'name': 'jobs', 'title': _('All Jobs') }]
def get_list_context(context):
context.title = _("Jobs")

View File

@@ -284,7 +284,7 @@ class ProcessPayroll(Document):
})
# Deductions
for acc, amount in deductions.items():
for acc, amt in deductions.items():
payable_amount -= flt(amount, precision)
accounts.append({
"account": acc,

View File

@@ -39,7 +39,6 @@ erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
args: {
method: 'erpnext.hr.doctype.upload_attendance.upload_attendance.upload'
},
no_socketio: true,
sample_url: "e.g. http://example.com/somefile.csv",
callback: function(attachment, r) {
var $log_wrapper = $(cur_frm.fields_dict.import_log.wrapper).empty();

View File

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

View File

@@ -95,8 +95,8 @@ class BOM(WebsiteGenerator):
self.validate_bom_currecny(item)
ret = self.get_bom_material_detail({
"item_code": item.item_code,
"item_name": item.item_name,
"item_code": item.item_code,
"item_name": item.item_name,
"bom_no": item.bom_no,
"stock_qty": item.stock_qty
})
@@ -128,7 +128,7 @@ class BOM(WebsiteGenerator):
'uom' : item and args['stock_uom'] or '',
'conversion_factor': 1,
'bom_no' : args['bom_no'],
'rate' : rate / self.conversion_rate if self.conversion_rate else rate,
'rate' : rate / self.conversion_rate,
'qty' : args.get("qty") or args.get("stock_qty") or 1,
'stock_qty' : args.get("qty") or args.get("stock_qty") or 1,
'base_rate' : rate
@@ -147,28 +147,22 @@ class BOM(WebsiteGenerator):
if arg.get('scrap_items'):
rate = self.get_valuation_rate(arg)
elif arg:
if arg.get('bom_no') and self.set_rate_of_sub_assembly_item_based_on_bom:
if self.rm_cost_as_per == 'Valuation Rate':
rate = self.get_valuation_rate(arg)
elif self.rm_cost_as_per == 'Last Purchase Rate':
rate = arg['last_purchase_rate'] \
or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")
elif self.rm_cost_as_per == "Price List":
if not self.buying_price_list:
frappe.throw(_("Please select Price List"))
rate = frappe.db.get_value("Item Price",
{"price_list": self.buying_price_list, "item_code": arg["item_code"]}, "price_list_rate")
price_list_currency = frappe.db.get_value("Price List", self.buying_price_list, "currency")
if price_list_currency != self.company_currency():
rate = flt(rate * self.conversion_rate)
if arg['bom_no'] and (not rate or self.set_rate_of_sub_assembly_item_based_on_bom):
rate = self.get_bom_unitcost(arg['bom_no'])
else:
if self.rm_cost_as_per == 'Valuation Rate':
rate = self.get_valuation_rate(arg)
elif self.rm_cost_as_per == 'Last Purchase Rate':
rate = arg.get('last_purchase_rate') \
or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")
elif self.rm_cost_as_per == "Price List":
if not self.buying_price_list:
frappe.throw(_("Please select Price List"))
rate = frappe.db.get_value("Item Price", {"price_list": self.buying_price_list,
"item_code": arg["item_code"]}, "price_list_rate")
price_list_currency = frappe.db.get_value("Price List",
self.buying_price_list, "currency")
if price_list_currency != self.company_currency():
rate = flt(rate * self.conversion_rate)
if not rate:
frappe.msgprint(_("{0} not found for Item {1}")
.format(self.rm_cost_as_per, arg["item_code"]))
return flt(rate)
@@ -318,7 +312,7 @@ class BOM(WebsiteGenerator):
li.append("{0} on row {1}".format(i.item_code, i.idx))
duplicate_list = '<br>' + '<br>'.join(li)
frappe.throw(_("Same item has been entered multiple times. {0}").format(duplicate_list))
frappe.throw(_("Same item has been entered multiple times. {list}").format(list=duplicate_list))
def check_recursion(self):
""" Check whether recursion occurs in any bom"""
@@ -352,7 +346,7 @@ class BOM(WebsiteGenerator):
count = 0
if not bom_list:
bom_list = []
if self.name not in bom_list:
bom_list.append(self.name)
@@ -380,7 +374,7 @@ class BOM(WebsiteGenerator):
if d.workstation:
if not d.hour_rate:
hour_rate = flt(frappe.db.get_value("Workstation", d.workstation, "hour_rate"))
d.hour_rate = hour_rate / flt(self.conversion_rate) if self.conversion_rate else hour_rate
d.hour_rate = hour_rate / flt(self.conversion_rate)
if d.hour_rate and d.time_in_mins:
d.base_hour_rate = flt(d.hour_rate) * flt(self.conversion_rate)

View File

@@ -30,13 +30,12 @@ class BOMUpdateTool(Document):
frappe.throw(_("The selected BOMs are not for the same item"))
def update_new_bom(self):
new_bom_unitcost = frappe.db.sql("""select total_cost/quantity
from `tabBOM` where name = %s""", self.new_bom)
new_bom_unitcost = flt(new_bom_unitcost[0][0]) if new_bom_unitcost else 0
current_bom_unitcost = frappe.db.sql("""select total_cost/quantity
from `tabBOM` where name = %s""", self.current_bom)
current_bom_unitcost = current_bom_unitcost and flt(current_bom_unitcost[0][0]) or 0
frappe.db.sql("""update `tabBOM Item` set bom_no=%s,
rate=%s, amount=stock_qty*%s where bom_no = %s and docstatus < 2""",
(self.new_bom, new_bom_unitcost, new_bom_unitcost, self.current_bom))
(self.new_bom, current_bom_unitcost, current_bom_unitcost, self.current_bom))
def get_parent_boms(self):
return [d[0] for d in frappe.db.sql("""select distinct parent

View File

@@ -51,9 +51,9 @@ class ProductionOrder(Document):
def validate_sales_order(self):
if self.sales_order:
so = frappe.db.sql("""
select so.name, so_item.delivery_date, so.project
select so.name, so_item.delivery_date, so.project
from `tabSales Order` so, `tabSales Order Item` so_item
where so.name=%s and so.name=so_item.parent
where so.name=%s and so.name=so_item.parent
and so.docstatus = 1 and so_item.item_code=%s
""", (self.sales_order, self.production_item), as_dict=1)
@@ -112,7 +112,7 @@ class ProductionOrder(Document):
allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings",
"over_production_allowance_percentage"))
if total_qty > so_qty + (allowance_percentage/100 * so_qty):
frappe.throw(_("Cannot produce more Item {0} than Sales Order quantity {1}")
.format(self.production_item, so_qty), OverProductionError)
@@ -217,27 +217,27 @@ class ProductionOrder(Document):
def set_production_order_operations(self):
"""Fetch operations from BOM and set in 'Production Order'"""
self.set('operations', [])
if not self.bom_no \
or cint(frappe.db.get_single_value("Manufacturing Settings", "disable_capacity_planning")):
return
if self.use_multi_level_bom:
bom_list = frappe.get_doc("BOM", self.bom_no).traverse_tree()
else:
bom_list = [self.bom_no]
operations = frappe.db.sql("""
select
select
operation, description, workstation, idx,
base_hour_rate as hour_rate, time_in_mins,
base_hour_rate as hour_rate, time_in_mins,
"Pending" as status, parent as bom
from
`tabBOM Operation`
where
parent in (%s) order by idx
""" % ", ".join(["%s"]*len(bom_list)), tuple(bom_list), as_dict=1)
self.set('operations', operations)
self.calculate_time()
@@ -277,7 +277,7 @@ class ProductionOrder(Document):
timesheet.set('time_logs', [])
for i, d in enumerate(self.operations):
if d.status != 'Completed':
self.set_start_end_time_for_workstation(d, i)
@@ -370,13 +370,8 @@ class ProductionOrder(Document):
self.actual_start_date = None
self.actual_end_date = None
if self.get("operations"):
actual_start_dates = [d.actual_start_time for d in self.get("operations") if d.actual_start_time]
if actual_start_dates:
self.actual_start_date = min(actual_start_dates)
actual_end_dates = [d.actual_end_time for d in self.get("operations") if d.actual_end_time]
if actual_end_dates:
self.actual_end_date = max(actual_end_dates)
self.actual_start_date = min([d.actual_start_time for d in self.get("operations")])
self.actual_end_date = max([d.actual_end_time for d in self.get("operations")])
def delete_timesheet(self):
for timesheet in frappe.get_all("Timesheet", ["name"], {"production_order": self.name}):
@@ -416,18 +411,18 @@ class ProductionOrder(Document):
if d.source_warehouse:
stock_bin = get_bin(d.item_code, d.source_warehouse)
stock_bin.update_reserved_qty_for_production()
def get_items_and_operations_from_bom(self):
self.set_required_items()
self.set_production_order_operations()
return check_if_scrap_warehouse_mandatory(self.bom_no)
def set_available_qty(self):
for d in self.get("required_items"):
if d.source_warehouse:
d.available_qty_at_source_warehouse = get_latest_stock_qty(d.item_code, d.source_warehouse)
if self.wip_warehouse:
d.available_qty_at_wip_warehouse = get_latest_stock_qty(d.item_code, self.wip_warehouse)
@@ -444,7 +439,7 @@ class ProductionOrder(Document):
'required_qty': item.qty,
'source_warehouse': item.source_warehouse or item.default_warehouse
})
self.set_available_qty()
def update_transaferred_qty_for_required_items(self):
@@ -468,12 +463,12 @@ class ProductionOrder(Document):
def get_item_details(item, project = None):
res = frappe.db.sql("""
select stock_uom, description
from `tabItem`
where disabled=0
from `tabItem`
where disabled=0
and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)
and name=%s
""", (nowdate(), item), as_dict=1)
if not res:
return {}
@@ -616,14 +611,14 @@ def make_new_timesheet(source_name, target_doc=None):
@frappe.whitelist()
def stop_unstop(production_order, status):
""" Called from client side on Stop/Unstop event"""
if not frappe.has_permission("Production Order", "write"):
frappe.throw(_("Not permitted"), frappe.PermissionError)
pro_order = frappe.get_doc("Production Order", production_order)
pro_order.update_status(status)
pro_order.update_planned_qty()
frappe.msgprint(_("Production Order has been {0}").format(status))
pro_order.notify_update()
return pro_order.status
return pro_order.status

View File

@@ -59,6 +59,8 @@ QUnit.test("test: production order", function (assert) {
// Confirm the production order timesheet, save and submit it
() => frappe.click_link("TS-00"),
() => frappe.timeout(1),
() => frappe.click_button("Save"),
() => frappe.timeout(1),
() => frappe.click_button("Submit"),
() => frappe.timeout(1),
() => frappe.click_button("Yes"),

View File

@@ -12,6 +12,10 @@ from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
from erpnext.manufacturing.doctype.production_order.production_order import get_item_details
class ProductionPlanningTool(Document):
def __init__(self, arg1, arg2=None):
super(ProductionPlanningTool, self).__init__(arg1, arg2)
self.item_dict = {}
def clear_table(self, table_name):
self.set(table_name, [])
@@ -394,9 +398,6 @@ class ProductionPlanningTool(Document):
return bom_wise_item_details
def make_items_dict(self, item_list):
if not getattr(self, "item_dict", None):
self.item_dict = {}
for i in item_list:
self.item_dict.setdefault(i[0], []).append([flt(i[1]), i[2], i[3], i[4], i[5]])

View File

@@ -1,27 +0,0 @@
<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>

View File

@@ -408,8 +408,6 @@ erpnext.patches.v8_0.update_stock_qty_value_in_bom_item
erpnext.patches.v8_0.update_sales_cost_in_project
erpnext.patches.v8_0.save_system_settings
erpnext.patches.v8_1.delete_deprecated_reports
erpnext.patches.v9_0.remove_subscription_module
erpnext.patches.v8_7.make_subscription_from_recurring_data
erpnext.patches.v8_1.setup_gst_india #2017-06-27
execute:frappe.reload_doc('regional', 'doctype', 'gst_hsn_code')
erpnext.patches.v8_1.removed_roles_from_gst_report_non_indian_account
@@ -434,20 +432,16 @@ erpnext.patches.v8_5.update_customer_group_in_POS_profile
erpnext.patches.v8_6.update_timesheet_company_from_PO
erpnext.patches.v8_6.set_write_permission_for_quotation_for_sales_manager
erpnext.patches.v8_5.remove_project_type_property_setter
erpnext.patches.v8_7.add_more_gst_fields #21-09-2017
erpnext.patches.v8_7.add_more_gst_fields
erpnext.patches.v8_7.fix_purchase_receipt_status
erpnext.patches.v8_6.rename_bom_update_tool
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
erpnext.patches.v8_9.rename_company_sales_target_field
erpnext.patches.v8_8.set_bom_rate_as_per_uom
erpnext.patches.v8_8.add_new_fields_in_accounts_settings
erpnext.patches.v9_0.remove_subscription_module
erpnext.patches.v8_7.make_subscription_from_recurring_data
erpnext.patches.v8_9.set_print_zero_amount_taxes
erpnext.patches.v8_9.set_default_customer_group
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.set_default_fields_in_variant_settings
erpnext.patches.v8_9.update_billing_gstin_for_indian_account
erpnext.patches.v9_0.fix_subscription_next_date
erpnext.patches.v9_0.add_healthcare_domain
erpnext.patches.v9_0.set_variant_item_description

View File

@@ -8,15 +8,9 @@ from frappe.utils import today
def execute():
frappe.reload_doc('accounts', 'doctype', 'subscription')
frappe.reload_doc('selling', 'doctype', 'sales_order')
frappe.reload_doc('selling', 'doctype', 'quotation')
frappe.reload_doc('buying', 'doctype', 'purchase_order')
frappe.reload_doc('buying', 'doctype', 'supplier_quotation')
frappe.reload_doc('accounts', 'doctype', 'sales_invoice')
frappe.reload_doc('accounts', 'doctype', 'purchase_invoice')
frappe.reload_doc('stock', 'doctype', 'purchase_receipt')
frappe.reload_doc('stock', 'doctype', 'delivery_note')
frappe.reload_doc('accounts', 'doctype', 'journal_entry')
frappe.reload_doc('accounts', 'doctype', 'payment_entry')
for doctype in ['Sales Order', 'Sales Invoice',
'Purchase Invoice', 'Purchase Invoice']:

View File

@@ -1,9 +0,0 @@
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')"
)

View File

@@ -9,27 +9,27 @@ def execute():
frappe.reload_doc("setup", "doctype", "setup_progress_action")
actions = [
{"action_name": "Add Company", "action_doctype": "Company", "min_doc_count": 1, "is_completed": 1,
{"action_name": _("Add Company"), "action_doctype": "Company", "min_doc_count": 1, "is_completed": 1,
"domains": '[]' },
{"action_name": "Set Sales Target", "action_doctype": "Company", "min_doc_count": 99,
{"action_name": _("Set Sales Target"), "action_doctype": "Company", "min_doc_count": 99,
"action_document": frappe.defaults.get_defaults().get("company") or '',
"action_field": "monthly_sales_target", "is_completed": 0,
"domains": '["Manufacturing", "Services", "Retail", "Distribution"]' },
{"action_name": "Add Customers", "action_doctype": "Customer", "min_doc_count": 1, "is_completed": 0,
{"action_name": _("Add Customers"), "action_doctype": "Customer", "min_doc_count": 1, "is_completed": 0,
"domains": '["Manufacturing", "Services", "Retail", "Distribution"]' },
{"action_name": "Add Suppliers", "action_doctype": "Supplier", "min_doc_count": 1, "is_completed": 0,
{"action_name": _("Add Suppliers"), "action_doctype": "Supplier", "min_doc_count": 1, "is_completed": 0,
"domains": '["Manufacturing", "Services", "Retail", "Distribution"]' },
{"action_name": "Add Products", "action_doctype": "Item", "min_doc_count": 1, "is_completed": 0,
{"action_name": _("Add Products"), "action_doctype": "Item", "min_doc_count": 1, "is_completed": 0,
"domains": '["Manufacturing", "Services", "Retail", "Distribution"]' },
{"action_name": "Add Programs", "action_doctype": "Program", "min_doc_count": 1, "is_completed": 0,
{"action_name": _("Add Programs"), "action_doctype": "Program", "min_doc_count": 1, "is_completed": 0,
"domains": '["Education"]' },
{"action_name": "Add Instructors", "action_doctype": "Instructor", "min_doc_count": 1, "is_completed": 0,
{"action_name": _("Add Instructors"), "action_doctype": "Instructor", "min_doc_count": 1, "is_completed": 0,
"domains": '["Education"]' },
{"action_name": "Add Courses", "action_doctype": "Course", "min_doc_count": 1, "is_completed": 0,
{"action_name": _("Add Courses"), "action_doctype": "Course", "min_doc_count": 1, "is_completed": 0,
"domains": '["Education"]' },
{"action_name": "Add Rooms", "action_doctype": "Room", "min_doc_count": 1, "is_completed": 0,
{"action_name": _("Add Rooms"), "action_doctype": "Room", "min_doc_count": 1, "is_completed": 0,
"domains": '["Education"]' },
{"action_name": "Add Users", "action_doctype": "User", "min_doc_count": 4, "is_completed": 0,
{"action_name": _("Add Users"), "action_doctype": "User", "min_doc_count": 4, "is_completed": 0,
"domains": '[]' }
]

View File

@@ -1,13 +0,0 @@
# 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()

View File

@@ -1,15 +0,0 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if company:
for doctype in ['Sales Invoice', 'Delivery Note']:
frappe.db.sql(""" update `tab{0}`
set billing_address_gstin = (select gstin from `tabAddress`
where name = customer_address)
where customer_address is not null and customer_address != ''""".format(doctype))

View File

@@ -1,14 +0,0 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
def execute():
domain = _('Healthcare')
if not frappe.db.exists('Domain', domain):
frappe.get_doc({
'doctype': 'Domain',
'domain': domain
}).insert(ignore_permissions=True)

View File

@@ -1,27 +0,0 @@
# 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_doctype('Subscription')
doctypes = ('Purchase Order', 'Sales Order', 'Purchase Invoice', 'Sales Invoice')
for data in frappe.get_all('Subscription', fields = ["name", "reference_doctype", "reference_document"],
filters = {'reference_doctype': ('in', doctypes)}):
doc = frappe.get_doc('Subscription', data.name)
fields = ['transaction_date']
if doc.reference_doctype in ['Sales Invoice', 'Purchase Invoice']:
fields = ['posting_date']
fields.extend(['from_date', 'to_date'])
reference_data = frappe.db.get_value(data.reference_doctype,
data.reference_document, fields, as_dict=1)
if reference_data:
doc.start_date = reference_data.get('posting_date') or reference_data.get('transaction_date')
doc.from_date = reference_data.get('from_date')
doc.to_date = reference_data.get('to_date')
doc.set_next_schedule_date()
doc.db_update()

View File

@@ -1,45 +0,0 @@
import frappe
from frappe.utils import cstr
def execute():
'''
Issue:
While copying data from template item to variant item,
the system appending description multiple times to the respective variant.
Purpose:
Check variant description,
if variant have user defined description remove all system appended descriptions
else replace multiple system generated descriptions with single description
Steps:
1. Get all variant items
2. Create system generated variant description
3. If variant have user defined description, remove all system generated descriptions
4. If variant description only contains system generated description,
replace multiple descriptions by new description.
'''
for item in frappe.db.sql(""" select name from tabItem
where ifnull(variant_of, '') != '' """,as_dict=1):
variant = frappe.get_doc("Item", item.name)
temp_variant_description = '\n'
if variant.attributes:
for d in variant.attributes:
temp_variant_description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"
variant_description = variant.description.replace(temp_variant_description, '').rstrip()
if variant_description:
splitted_desc = variant.description.strip().split(temp_variant_description)
if len(splitted_desc) > 2:
if splitted_desc[0] == '':
variant_description = temp_variant_description + variant_description
elif splitted_desc[1] == '' or splitted_desc[1] == '\n':
variant_description += temp_variant_description
variant.db_set('description', variant_description, update_modified=False)
else:
variant.db_set('description', variant_description, update_modified=False)
else:
variant.db_set('description', temp_variant_description, update_modified=False)

View File

@@ -53,17 +53,12 @@ class Project(Document):
return frappe.get_all("Task", "*", {"project": self.name}, order_by="exp_start_date asc")
def validate(self):
self.validate_project_name()
self.validate_dates()
self.validate_weights()
self.sync_tasks()
self.tasks = []
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):
if self.expected_start_date and self.expected_end_date:
if getdate(self.expected_end_date) < getdate(self.expected_start_date):

View File

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

View File

@@ -159,14 +159,9 @@ frappe.ui.form.on("Timesheet Detail", {
});
var calculate_end_time = function(frm, cdt, cdn) {
let child = locals[cdt][cdn];
var child = locals[cdt][cdn];
if(!child.from_time) {
// if from_time value is not available then set the current datetime
frappe.model.set_value(cdt, cdn, "from_time", frappe.datetime.get_datetime_as_string());
}
let d = moment(child.from_time);
var d = moment(child.from_time);
if(child.hours) {
d.add(child.hours, "hours");
frm._setting_hours = true;

View File

@@ -1,45 +1,39 @@
frappe.ui.form.on("Communication", {
refresh: (frm) => {
// setup custom Make button only if Communication is Email
if(frm.doc.communication_medium == "Email" && frm.doc.sent_or_received == "Received") {
frm.events.setup_custom_buttons(frm);
}
},
setup_custom_buttons: (frm) => {
let confirm_msg = "Are you sure you want to create {0} from this email";
refresh: function(frm) {
if(frm.doc.reference_doctype !== "Issue") {
frm.add_custom_button(__("Issue"), () => {
frappe.confirm(__(confirm_msg, [__("Issue")]), () => {
frm.add_custom_button(__("Issue"), function() {
frappe.confirm("Are you sure you want to create Issue from this email", function(){
frm.trigger('make_issue_from_communication');
})
}, "Make");
}
if(!in_list(["Lead", "Opportunity"], frm.doc.reference_doctype)) {
frm.add_custom_button(__("Lead"), () => {
frappe.confirm(__(confirm_msg, [__("Lead")]), () => {
frm.add_custom_button(__("Lead"), function() {
frappe.confirm("Are you sure you want to create Lead from this email", function(){
frm.trigger('make_lead_from_communication');
})
}, "Make");
frm.add_custom_button(__("Opportunity"), () => {
frappe.confirm(__(confirm_msg, [__("Opportunity")]), () => {
frm.add_custom_button(__("Opportunity"), function() {
frappe.confirm("Are you sure you want to create Opportunity from this email", function(){
frm.trigger('make_opportunity_from_communication');
})
}, "Make");
}
frm.page.set_inner_btn_group_as_primary(__("Make"));
},
make_lead_from_communication: (frm) => {
make_lead_from_communication: function(frm) {
return frappe.call({
method: "frappe.email.inbox.make_lead_from_communication",
args: {
communication: frm.doc.name
},
freeze: true,
callback: (r) => {
callback: function(r) {
if(r.message) {
frm.reload_doc()
}
@@ -47,14 +41,14 @@ frappe.ui.form.on("Communication", {
})
},
make_issue_from_communication: (frm) => {
make_issue_from_communication: function(frm) {
return frappe.call({
method: "frappe.email.inbox.make_issue_from_communication",
args: {
communication: frm.doc.name
},
freeze: true,
callback: (r) => {
callback: function(r) {
if(r.message) {
frm.reload_doc()
}
@@ -62,14 +56,14 @@ frappe.ui.form.on("Communication", {
})
},
make_opportunity_from_communication: (frm) => {
make_opportunity_from_communication: function(frm) {
return frappe.call({
method: "frappe.email.inbox.make_opportunity_from_communication",
args: {
communication: frm.doc.name
},
freeze: true,
callback: (r) => {
callback: function(r) {
if(r.message) {
frm.reload_doc()
}

View File

@@ -101,6 +101,27 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
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"]) {
this["taxes_remove"] = this.calculate_taxes_and_totals;
@@ -132,36 +153,11 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
return {
filters: filters
};
}
});
}
},
onload: function() {
var me = this;
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() {
@@ -199,12 +195,13 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
},
onload_post_render: function() {
var me = this;
if(this.frm.doc.__islocal && !(this.frm.doc.taxes || []).length
&& !(this.frm.doc.__onload ? this.frm.doc.__onload.load_after_mapping : false)) {
frappe.after_ajax(() => this.apply_default_taxes());
this.apply_default_taxes();
} else if(this.frm.doc.__islocal && this.frm.doc.company && this.frm.doc["items"]
&& !this.frm.doc.is_pos) {
frappe.after_ajax(() => this.calculate_taxes_and_totals());
me.calculate_taxes_and_totals();
}
if(frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "item_code")) {
this.setup_item_selector();
@@ -381,7 +378,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
if(me.frm.doc.company && me.frm.fields_dict.currency) {
var company_currency = me.get_company_currency();
var company_doc = frappe.get_doc(":Company", me.frm.doc.company);
if (!me.frm.doc.currency) {
me.frm.set_value("currency", company_currency);
}
@@ -523,7 +519,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
},
conversion_rate: function() {
const me = this.frm;
if(this.frm.doc.currency === this.get_company_currency()) {
this.frm.set_value("conversion_rate", 1.0);
}
@@ -541,12 +536,6 @@ 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() {

View File

@@ -37,6 +37,7 @@
<div class="cell price-cell text-right tax-table">
</div>
</div>
{% if (apply_discount_on) { %}
<div class="pos-list-row discount-amount-area">
<div class="cell"></div>
<div class="cell text-right">{%= __("Discount") %}</div>
@@ -51,6 +52,7 @@
</div>
</div>
</div>
{% } %}
<div class="pos-list-row grand-total-area collapse-btn" style="border-bottom:1px solid #d1d8dd;">
<div class="cell">
<a class="">

View File

@@ -86,10 +86,6 @@ erpnext.setup.slides_settings = [
});
},
validate: function() {
if ((this.values.company_name || "").toLowerCase() == "company") {
frappe.msgprint(__("Company Name cannot be Company"));
return false;
}
if (!this.values.company_abbr) {
return false;
}
@@ -139,6 +135,10 @@ erpnext.setup.slides_settings = [
frappe.msgprint(__("Please enter valid Financial Year Start and End Dates"));
return false;
}
if ((this.values.company_name || "").toLowerCase() == "company") {
frappe.msgprint(__("Company Name cannot be Company"));
return false;
}
return true;
},

View File

@@ -96,17 +96,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
if(this.show_dialog) {
let d = this.item;
if (d.has_serial_no && d.serial_no) {
this.dialog.set_value('serial_no', d.serial_no);
} else if (d.batch_no) {
this.dialog.fields_dict.batches.df.data.push({
'batch_no': d.batch_no,
'actual_qty': d.actual_qty,
'selected_qty': d.qty
});
this.dialog.fields_dict.batches.grid.refresh();
}
this.dialog.set_value('serial_no', d.serial_no);
}
this.dialog.show();
@@ -126,10 +116,8 @@ erpnext.SerialNoBatchSelector = Class.extend({
}
values.batches.map((batch, i) => {
if(!batch.selected_qty || batch.selected_qty === 0 ) {
if (!this.show_dialog) {
frappe.throw(__("Please select quantity on row " + (i+1)));
return false;
}
frappe.throw(__("Please select quantity on row " + (i+1)));
return false;
}
});
return true;
@@ -137,11 +125,9 @@ erpnext.SerialNoBatchSelector = Class.extend({
} else {
let serial_nos = values.serial_no || '';
if (!serial_nos || !serial_nos.replace(/\s/g, '').length) {
if (!this.show_dialog) {
frappe.throw(__("Please enter serial numbers for serialized item "
+ values.item_code));
return false;
}
frappe.throw(__("Please enter serial numbers for serialized item "
+ values.item_code));
return false;
}
return true;
}

View File

@@ -84,13 +84,34 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-09-29 14:38:52.220743",
"modified_by": "Administrator",
"modified": "2017-08-31 14:38:52.220743",
"modified_by": "ewdszx@ed.ews",
"module": "Regional",
"name": "GST HSN Code",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,

View File

@@ -83,13 +83,34 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2017-09-29 14:39:15.625952",
"modified_by": "Administrator",
"modified": "2017-08-31 14:39:15.625952",
"modified_by": "ewdszx@ed.ews",
"module": "Regional",
"name": "GST Settings",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"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
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,

View File

@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe, os, json
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
from frappe.permissions import add_permission
from erpnext.regional.india import states
@@ -12,7 +12,7 @@ def setup(company=None, patch=True):
make_custom_fields()
add_permissions()
add_custom_roles_for_reports()
frappe.enqueue('erpnext.regional.india.setup.add_hsn_sac_codes', now=frappe.flags.in_test)
add_hsn_sac_codes()
add_print_formats()
if not patch:
update_address_template()
@@ -47,14 +47,12 @@ def add_hsn_sac_codes():
def create_hsn_codes(data, code_field):
for d in data:
hsn_code = frappe.new_doc('GST HSN Code')
hsn_code.description = d["description"]
hsn_code.hsn_code = d[code_field]
hsn_code.name = d[code_field]
try:
if not frappe.db.exists("GST HSN Code", d[code_field]):
hsn_code = frappe.new_doc('GST HSN Code')
hsn_code.description = d["description"]
hsn_code.hsn_code = d[code_field]
hsn_code.name = d[code_field]
hsn_code.db_insert()
except frappe.DuplicateEntryError:
pass
def add_custom_roles_for_reports():
for report_name in ('GST Sales Register', 'GST Purchase Register',
@@ -72,6 +70,7 @@ def add_custom_roles_for_reports():
def add_permissions():
for doctype in ('GST HSN Code', 'GST Settings'):
add_permission(doctype, 'Accounts Manager', 0)
add_permission(doctype, 'All', 0)
def add_print_formats():
@@ -112,15 +111,12 @@ def make_custom_fields():
]
sales_invoice_gst_fields = [
dict(fieldname='billing_address_gstin', label='Billing Address GSTIN',
fieldtype='Data', insert_after='customer_address',
options='customer_address.gstin', print_hide=1),
dict(fieldname='customer_gstin', label='Customer GSTIN',
fieldtype='Data', insert_after='shipping_address',
options='shipping_address_name.gstin', print_hide=1),
dict(fieldname='place_of_supply', label='Place of Supply',
fieldtype='Data', insert_after='customer_gstin', print_hide=1,
options='shipping_address_name.gst_state_number', read_only=0),
options='shipping_address_name.gst_state_number', read_only=1),
dict(fieldname='company_gstin', label='Company GSTIN',
fieldtype='Data', insert_after='company_address',
options='company_address.gstin', print_hide=1)
@@ -152,7 +148,11 @@ def make_custom_fields():
'Purchase Invoice Item': [hsn_sac_field]
}
create_custom_fields(custom_fields)
for dt, data in custom_fields.iteritems():
for df in data:
create_custom_field(dt, df)
# create_custom_fields(custom_fields)
def make_fixtures():
docs = [

View File

@@ -8,7 +8,6 @@ from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register i
def execute(filters=None):
return _execute(filters, additional_table_columns=[
dict(fieldtype='Data', label='Customer GSTIN', width=120),
dict(fieldtype='Data', label='Billing Address GSTIN', width=140),
dict(fieldtype='Data', label='Company GSTIN', width=120),
dict(fieldtype='Data', label='Place of Supply', width=120),
dict(fieldtype='Data', label='Reverse Charge', width=120),
@@ -18,7 +17,6 @@ def execute(filters=None):
dict(fieldtype='Data', label='HSN Code', width=120)
], additional_query_columns=[
'customer_gstin',
'billing_address_gstin',
'company_gstin',
'place_of_supply',
'reverse_charge',

View File

@@ -8,7 +8,6 @@ from erpnext.accounts.report.sales_register.sales_register import _execute
def execute(filters=None):
return _execute(filters, additional_table_columns=[
dict(fieldtype='Data', label='Customer GSTIN', width=120),
dict(fieldtype='Data', label='Billing Address GSTIN', width=140),
dict(fieldtype='Data', label='Company GSTIN', width=120),
dict(fieldtype='Data', label='Place of Supply', width=120),
dict(fieldtype='Data', label='Reverse Charge', width=120),
@@ -17,7 +16,6 @@ def execute(filters=None):
dict(fieldtype='Data', label='E-Commerce GSTIN', width=130)
], additional_query_columns=[
'customer_gstin',
'billing_address_gstin',
'company_gstin',
'place_of_supply',
'reverse_charge',

View File

@@ -50,7 +50,6 @@ class Fees(AccountsController):
select g.email_address
from `tabGuardian` g, `tabStudent Guardian` sg
where g.name = sg.guardian and sg.parent = %s and sg.parenttype = 'Student'
and ifnull(g.email_address, '')!=''
""", self.student)
student_email_id = frappe.db.get_value("Student", self.student, "student_email_id")

View File

@@ -13,6 +13,7 @@ class StudentApplicant(Document):
if self.student_admission:
naming_series = frappe.db.get_value('Student Admission', self.student_admission,
'naming_series_for_student_applicant')
print(naming_series)
if naming_series:
self.naming_series = naming_series

View File

@@ -12,8 +12,8 @@ from erpnext.stock.utils import get_valid_serial_nos
from erpnext.utilities.transaction_base import TransactionBase
class InstallationNote(TransactionBase):
def __init__(self, *args, **kwargs):
super(InstallationNote, self).__init__(*args, **kwargs)
def __init__(self, arg1, arg2=None):
super(InstallationNote, self).__init__(arg1, arg2)
self.status_updater = [{
'source_dt': 'Installation Note Item',
'target_dt': 'Delivery Note Item',

View File

@@ -32,7 +32,7 @@ class Quotation(SellingController):
self.validate_valid_till()
if self.items:
self.with_items = 1
def validate_valid_till(self):
if self.valid_till and self.valid_till < self.transaction_date:
frappe.throw(_("Valid till date cannot be before transaction date"))
@@ -79,10 +79,15 @@ class Quotation(SellingController):
else:
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):
self.check_item_table()
# 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
self.update_opportunity()

View File

@@ -684,7 +684,7 @@
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"precision": "2",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -1583,7 +1583,7 @@
"istable": 1,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-09-27 08:31:37.485134",
"modified": "2017-05-10 17:14:45.736424",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation Item",

Some files were not shown because too many files have changed in this diff Show More