mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-04 02:04:37 +00:00
Compare commits
205 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bdfc074a7d | ||
|
|
c7bd77111e | ||
|
|
4a60554b91 | ||
|
|
3dc21b099d | ||
|
|
40a02769c5 | ||
|
|
e6712c129c | ||
|
|
464289b726 | ||
|
|
1c1237537b | ||
|
|
b7483f6dfd | ||
|
|
cba7a11d75 | ||
|
|
2672c331af | ||
|
|
f4283a3ebf | ||
|
|
8a0943ead7 | ||
|
|
82c9352d53 | ||
|
|
311823aca1 | ||
|
|
6c06e700d6 | ||
|
|
b88b7eaf5b | ||
|
|
e7eda65968 | ||
|
|
5fdd26f1e7 | ||
|
|
c44290955e | ||
|
|
12e817bad6 | ||
|
|
47e405516b | ||
|
|
4272483f2d | ||
|
|
3173be9b17 | ||
|
|
9cb817874d | ||
|
|
8227422124 | ||
|
|
a7d5f94d4a | ||
|
|
7624e7bf85 | ||
|
|
3e4ca4219f | ||
|
|
5ab6ff2470 | ||
|
|
a516856c32 | ||
|
|
a3fe5b8528 | ||
|
|
45a640df08 | ||
|
|
80f333950b | ||
|
|
d3214fed57 | ||
|
|
bbbce58884 | ||
|
|
8bf7230fcc | ||
|
|
496174bbdf | ||
|
|
8854438830 | ||
|
|
15411fcf70 | ||
|
|
76a9cefc9c | ||
|
|
4f7873b9df | ||
|
|
ad9afe68f3 | ||
|
|
41e2b98b63 | ||
|
|
4eb5286c4b | ||
|
|
3f309e1c20 | ||
|
|
9bb81ee1cb | ||
|
|
4cdb9dee09 | ||
|
|
ee9da67173 | ||
|
|
e78601b040 | ||
|
|
47b2a5f7d1 | ||
|
|
5ba4c4c49c | ||
|
|
f347e23556 | ||
|
|
fbbb8695f3 | ||
|
|
f9577652a0 | ||
|
|
d270748eea | ||
|
|
2622d370c6 | ||
|
|
e181dd4c24 | ||
|
|
7f9d75521e | ||
|
|
eaf0abedd4 | ||
|
|
6f5853b97a | ||
|
|
62ce218fc2 | ||
|
|
1b0f3ec666 | ||
|
|
beeba8b37a | ||
|
|
d7636b2b19 | ||
|
|
b870d0081b | ||
|
|
e0dfd1608e | ||
|
|
607b5d4985 | ||
|
|
f23788bb7d | ||
|
|
671c6610de | ||
|
|
8c3d19e2ab | ||
|
|
4a5ac7cea6 | ||
|
|
f7a856b913 | ||
|
|
7d4fd35aa3 | ||
|
|
e87a076f1d | ||
|
|
ff689a658f | ||
|
|
b290e3a4e7 | ||
|
|
77f0822abe | ||
|
|
f83bc51e81 | ||
|
|
fc712aea32 | ||
|
|
7105c4b76c | ||
|
|
54c725dcd1 | ||
|
|
1e2c554e61 | ||
|
|
65dfd09947 | ||
|
|
61287e3c53 | ||
|
|
1b67d71139 | ||
|
|
32456b0f14 | ||
|
|
3d0d4b2157 | ||
|
|
7e5a9f5c0e | ||
|
|
d3e21fff66 | ||
|
|
5171956646 | ||
|
|
0cc93538ed | ||
|
|
b779644493 | ||
|
|
c83e793ce8 | ||
|
|
4d68e03a97 | ||
|
|
eaec4695f7 | ||
|
|
5049edb494 | ||
|
|
d17bea0a31 | ||
|
|
444bfff1ff | ||
|
|
ee4a2dd26f | ||
|
|
f6580268e6 | ||
|
|
e3a468ed1b | ||
|
|
9e6f2a49e8 | ||
|
|
445e8a2e57 | ||
|
|
7b6eaee05b | ||
|
|
c26e3f1569 | ||
|
|
c4e52e5f95 | ||
|
|
7eba1a35d3 | ||
|
|
1f10d693e9 | ||
|
|
53e8989699 | ||
|
|
8919669ac2 | ||
|
|
d977333a99 | ||
|
|
3a2834c7ad | ||
|
|
88491715e0 | ||
|
|
0bdf1e5ef1 | ||
|
|
f059e7be35 | ||
|
|
00a48ad4e5 | ||
|
|
def308a433 | ||
|
|
cb38e599e5 | ||
|
|
9b98d7fa14 | ||
|
|
b1bf502119 | ||
|
|
cb48404bd2 | ||
|
|
5b58e489a8 | ||
|
|
4b99fe15cc | ||
|
|
b9fe14631d | ||
|
|
01b96a0e31 | ||
|
|
79a1d2a3b0 | ||
|
|
6488645d42 | ||
|
|
b46900a4cb | ||
|
|
912ae24ca2 | ||
|
|
e292c83114 | ||
|
|
e1a4b3e4bc | ||
|
|
d3a48a83fd | ||
|
|
80d24f83f8 | ||
|
|
5d8fd477bd | ||
|
|
bf37995745 | ||
|
|
5510d0751d | ||
|
|
b79c4a9ff6 | ||
|
|
1b61dfd9ea | ||
|
|
3f7d96ecba | ||
|
|
367b90e3ae | ||
|
|
945f502748 | ||
|
|
9c339145b2 | ||
|
|
3c14c5a16c | ||
|
|
bdb4c542e7 | ||
|
|
6d61a45f42 | ||
|
|
c314485d55 | ||
|
|
3b04cfc812 | ||
|
|
79ba422273 | ||
|
|
0dc3c1b114 | ||
|
|
0f6fff6f0c | ||
|
|
2b87d100fa | ||
|
|
c4ee77a3cc | ||
|
|
c9f9e5235b | ||
|
|
edd4fd4692 | ||
|
|
ca916a73de | ||
|
|
53e19075d1 | ||
|
|
a0ba5594f9 | ||
|
|
16645803f9 | ||
|
|
94799a8b93 | ||
|
|
fce14fdcf0 | ||
|
|
7baacb7f74 | ||
|
|
76615c8001 | ||
|
|
283922daa2 | ||
|
|
da8de2f0c7 | ||
|
|
0060993eab | ||
|
|
566a0a05c8 | ||
|
|
f702d72c35 | ||
|
|
76e1ca35ad | ||
|
|
e0a845c356 | ||
|
|
eb686f8b1a | ||
|
|
d3fa19143d | ||
|
|
70eaf2da95 | ||
|
|
103b239a31 | ||
|
|
87e994e0a2 | ||
|
|
45d45f4247 | ||
|
|
946e182564 | ||
|
|
9aff73d156 | ||
|
|
df8fbd7d72 | ||
|
|
51a07d19c8 | ||
|
|
8ff0a64ac2 | ||
|
|
c0b4f0e81b | ||
|
|
b0ba55e52f | ||
|
|
d4c2d77bba | ||
|
|
ada8ba96b3 | ||
|
|
773e686fb1 | ||
|
|
afe9eabd3c | ||
|
|
cb5e1e550f | ||
|
|
3b61552836 | ||
|
|
4ebac3380d | ||
|
|
cdd6ded790 | ||
|
|
fb142f5283 | ||
|
|
6d78f7b862 | ||
|
|
5478a7fa67 | ||
|
|
2851dfad99 | ||
|
|
5b05335e89 | ||
|
|
0e28fccb34 | ||
|
|
ab5b03011d | ||
|
|
cb6774e373 | ||
|
|
1276893550 | ||
|
|
27bbb561d2 | ||
|
|
2c1f44ecfa | ||
|
|
c73383c34c | ||
|
|
764bb30d2d | ||
|
|
6544a85f1e |
@@ -4,7 +4,7 @@ import inspect
|
||||
import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
|
||||
__version__ = '8.11.6'
|
||||
__version__ = '9.1.8'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
||||
@@ -286,6 +286,99 @@
|
||||
"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,
|
||||
@@ -299,7 +392,7 @@
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-06-16 17:39:50.614522",
|
||||
"modified": "2017-09-05 10:10:03.117505",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounts Settings",
|
||||
|
||||
@@ -5,10 +5,20 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import cint, comma_and
|
||||
from frappe.utils import cint
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class AccountsSettings(Document):
|
||||
def on_update(self):
|
||||
pass
|
||||
pass
|
||||
|
||||
def validate(self):
|
||||
self.validate_stale_days()
|
||||
|
||||
def validate_stale_days(self):
|
||||
if not self.allow_stale and cint(self.stale_days) <= 0:
|
||||
frappe.msgprint(
|
||||
"Stale Days should start from 1.", title='Error', indicator='red',
|
||||
raise_exception=1)
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
QUnit.module('accounts');
|
||||
|
||||
QUnit.test("test: Accounts Settings doesn't allow negatives", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
assert.expect(2);
|
||||
|
||||
frappe.run_serially([
|
||||
() => frappe.set_route('Form', 'Accounts Settings', 'Accounts Settings'),
|
||||
() => frappe.timeout(2),
|
||||
() => unchecked_if_checked(cur_frm, 'Allow Stale Exchange Rates', frappe.click_check),
|
||||
() => cur_frm.set_value('stale_days', 0),
|
||||
() => frappe.click_button('Save'),
|
||||
() => frappe.timeout(2),
|
||||
() => {
|
||||
assert.ok(cur_dialog);
|
||||
},
|
||||
() => frappe.click_button('Close'),
|
||||
() => cur_frm.set_value('stale_days', -1),
|
||||
() => frappe.click_button('Save'),
|
||||
() => frappe.timeout(2),
|
||||
() => {
|
||||
assert.ok(cur_dialog);
|
||||
},
|
||||
() => frappe.click_button('Close'),
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
||||
|
||||
const unchecked_if_checked = function(frm, field_name, fn){
|
||||
if (frm.doc.allow_stale) {
|
||||
return fn(field_name);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
|
||||
|
||||
class TestAccountsSettings(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
# Just in case `save` method succeeds, we need to take things back to default so that other tests
|
||||
# don't break
|
||||
cur_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||
cur_settings.allow_stale = 1
|
||||
cur_settings.save()
|
||||
|
||||
def test_stale_days(self):
|
||||
cur_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||
cur_settings.allow_stale = 0
|
||||
cur_settings.stale_days = 0
|
||||
|
||||
self.assertRaises(frappe.ValidationError, cur_settings.save)
|
||||
|
||||
cur_settings.stale_days = -1
|
||||
self.assertRaises(frappe.ValidationError, cur_settings.save)
|
||||
@@ -12,8 +12,8 @@ from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amo
|
||||
from erpnext.hr.doctype.employee_loan.employee_loan import update_disbursement_status
|
||||
|
||||
class JournalEntry(AccountsController):
|
||||
def __init__(self, arg1, arg2=None):
|
||||
super(JournalEntry, self).__init__(arg1, arg2)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(JournalEntry, self).__init__(*args, **kwargs)
|
||||
|
||||
def get_feed(self):
|
||||
return self.voucher_type
|
||||
@@ -54,7 +54,7 @@ class JournalEntry(AccountsController):
|
||||
def update_advance_paid(self):
|
||||
advance_paid = frappe._dict()
|
||||
for d in self.get("accounts"):
|
||||
if d.is_advance:
|
||||
if d.is_advance == "Yes":
|
||||
if d.reference_type in ("Sales Order", "Purchase Order"):
|
||||
advance_paid.setdefault(d.reference_type, []).append(d.reference_name)
|
||||
|
||||
@@ -76,7 +76,7 @@ class JournalEntry(AccountsController):
|
||||
|
||||
def unlink_advance_entry_reference(self):
|
||||
for d in self.get("accounts"):
|
||||
if d.is_advance and d.reference_type in ("Sales Invoice", "Purchase Invoice"):
|
||||
if d.is_advance == "Yes" and d.reference_type in ("Sales Invoice", "Purchase Invoice"):
|
||||
doc = frappe.get_doc(d.reference_type, d.reference_name)
|
||||
doc.delink_advance_entries(self.name)
|
||||
d.reference_type = ''
|
||||
|
||||
@@ -403,6 +403,9 @@ frappe.ui.form.on('Payment Entry', {
|
||||
|
||||
frm.events.set_difference_amount(frm);
|
||||
}
|
||||
|
||||
// Make read only if Accounts Settings doesn't allow stale rates
|
||||
frm.set_df_property("source_exchange_rate", "read_only", erpnext.stale_rate_allowed());
|
||||
},
|
||||
|
||||
target_exchange_rate: function(frm) {
|
||||
@@ -421,6 +424,9 @@ 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
|
||||
frm.set_df_property("target_exchange_rate", "read_only", erpnext.stale_rate_allowed());
|
||||
},
|
||||
|
||||
paid_amount: function(frm) {
|
||||
@@ -646,8 +652,15 @@ frappe.ui.form.on('Payment Entry', {
|
||||
var party_amount = frm.doc.payment_type=="Receive" ?
|
||||
frm.doc.paid_amount : frm.doc.received_amount;
|
||||
|
||||
var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [],
|
||||
function(d) { return flt(d.amount) }));
|
||||
|
||||
if(frm.doc.total_allocated_amount < party_amount) {
|
||||
unallocated_amount = party_amount - frm.doc.total_allocated_amount;
|
||||
if(frm.doc.payment_type == "Receive") {
|
||||
unallocated_amount = party_amount - (frm.doc.total_allocated_amount - total_deductions);
|
||||
} else {
|
||||
unallocated_amount = party_amount - (frm.doc.total_allocated_amount + total_deductions);
|
||||
}
|
||||
}
|
||||
}
|
||||
frm.set_value("unallocated_amount", unallocated_amount);
|
||||
@@ -666,9 +679,6 @@ frappe.ui.form.on('Payment Entry', {
|
||||
difference_amount = flt(frm.doc.base_paid_amount) - flt(frm.doc.base_received_amount);
|
||||
}
|
||||
|
||||
var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [],
|
||||
function(d) { return flt(d.amount) }));
|
||||
|
||||
frm.set_value("difference_amount", difference_amount - total_deductions);
|
||||
|
||||
frm.events.hide_unhide_fields(frm);
|
||||
|
||||
@@ -285,8 +285,13 @@ class PaymentEntry(AccountsController):
|
||||
if self.party:
|
||||
party_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount
|
||||
|
||||
total_deductions = sum([flt(d.amount) for d in self.get("deductions")])
|
||||
|
||||
if self.total_allocated_amount < party_amount:
|
||||
self.unallocated_amount = party_amount - self.total_allocated_amount
|
||||
if self.payment_type == "Receive":
|
||||
self.unallocated_amount = party_amount - (self.total_allocated_amount - total_deductions)
|
||||
else:
|
||||
self.unallocated_amount = party_amount - (self.total_allocated_amount + total_deductions)
|
||||
|
||||
def set_difference_amount(self):
|
||||
base_unallocated_amount = flt(self.unallocated_amount) * (flt(self.source_exchange_rate)
|
||||
@@ -765,6 +770,7 @@ 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,
|
||||
|
||||
@@ -296,7 +296,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-09-04 17:37:01.192312",
|
||||
"modified": "2017-10-16 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@ class PaymentReconciliation(Document):
|
||||
return payment_entries
|
||||
|
||||
def get_jv_entries(self):
|
||||
dr_or_cr = self.get_dr_or_cr()
|
||||
dr_or_cr = "credit_in_account_currency" if self.party_type == "Customer" \
|
||||
else "debit_in_account_currency"
|
||||
|
||||
bank_account_condition = "t2.against_account like %(bank_cash_account)s" \
|
||||
if self.bank_cash_account else "1=1"
|
||||
@@ -72,13 +73,13 @@ class PaymentReconciliation(Document):
|
||||
row = self.append('payments', {})
|
||||
row.update(e)
|
||||
|
||||
def get_invoice_entries(self, paying_party=False):
|
||||
def get_invoice_entries(self):
|
||||
#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, paying_party=paying_party)
|
||||
self.receivable_payable_account, condition=condition)
|
||||
|
||||
self.add_invoice_entries(non_reconciled_invoices)
|
||||
|
||||
@@ -102,7 +103,8 @@ class PaymentReconciliation(Document):
|
||||
|
||||
self.get_invoice_entries()
|
||||
self.validate_invoice()
|
||||
dr_or_cr = self.get_dr_or_cr()
|
||||
dr_or_cr = "credit_in_account_currency" \
|
||||
if self.party_type == "Customer" else "debit_in_account_currency"
|
||||
|
||||
lst = []
|
||||
for e in self.get('payments'):
|
||||
@@ -182,12 +184,3 @@ 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"
|
||||
|
||||
@@ -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'}, '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);
|
||||
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);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -3,7 +3,17 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class POSSettings(Document):
|
||||
pass
|
||||
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)
|
||||
@@ -1023,7 +1023,7 @@
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "2",
|
||||
"precision": "",
|
||||
"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-08-31 16:34:41.614743",
|
||||
"modified": "2017-09-27 08:31:38.432574",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Pricing Rule",
|
||||
|
||||
@@ -348,7 +348,6 @@ 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",
|
||||
|
||||
@@ -3440,139 +3440,13 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"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_depends_on": "is_recurring",
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.docstatus<2 && !doc.__islocal",
|
||||
"fieldname": "recurring_invoice",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Recurring Invoice",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "fa fa-time",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.docstatus<2",
|
||||
"description": "",
|
||||
"fieldname": "is_recurring",
|
||||
"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": "Is Recurring",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "Select the period when the invoice will be generated automatically",
|
||||
"fieldname": "recurring_type",
|
||||
"fieldtype": "Select",
|
||||
"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": "Recurring Type",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Monthly\nQuarterly\nHalf-yearly\nYearly",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"depends_on": "",
|
||||
"description": "Start date of current invoice's period",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
@@ -3603,7 +3477,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"depends_on": "",
|
||||
"description": "End date of current invoice's period",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
@@ -3628,138 +3502,13 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"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": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "notify_by_email",
|
||||
"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": "Notify by email",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The day of the month on which auto invoice will be generated e.g. 05, 28 etc",
|
||||
"fieldname": "repeat_on_day_of_month",
|
||||
"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": "Repeat on Day of Month",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which recurring invoice will be stop",
|
||||
"fieldname": "end_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": "End Date",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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_82",
|
||||
"fieldname": "column_break_114",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@@ -3771,101 +3520,8 @@
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which next invoice will be generated. It is generated on submit.",
|
||||
"fieldname": "next_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 Date",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"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": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The unique id for tracking all recurring invoices. It is generated on submit.",
|
||||
"fieldname": "recurring_id",
|
||||
"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": "Recurring Id",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"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": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "Enter Email Address separated by commas, invoice will be mailed automatically on particular date",
|
||||
"fieldname": "notification_email_address",
|
||||
"fieldtype": "Small 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": "Notification Email Address",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
@@ -3881,8 +3537,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"fieldname": "recurring_print_format",
|
||||
"fieldname": "subscription",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@@ -3891,15 +3546,15 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Recurring Print Format",
|
||||
"label": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Print Format",
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"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,
|
||||
@@ -3920,7 +3575,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-09-19 11:22:47.074420",
|
||||
"modified": "2017-10-24 12:51:51.199594",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
||||
@@ -22,8 +22,8 @@ form_grid_templates = {
|
||||
}
|
||||
|
||||
class PurchaseInvoice(BuyingController):
|
||||
def __init__(self, arg1, arg2=None):
|
||||
super(PurchaseInvoice, self).__init__(arg1, arg2)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PurchaseInvoice, self).__init__(*args, **kwargs)
|
||||
self.status_updater = [{
|
||||
'source_dt': 'Purchase Invoice Item',
|
||||
'target_dt': 'Purchase Order Item',
|
||||
|
||||
@@ -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') if pos_profile.get('apply_discount') else ''
|
||||
doc.apply_discount_on = pos_profile.get('apply_discount_on') or 'Grand Total'
|
||||
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,6 +417,7 @@ 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):
|
||||
@@ -441,6 +442,7 @@ 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):
|
||||
@@ -484,17 +486,21 @@ 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(e, si_doc, name, name_list)
|
||||
name_list = save_invoice(doc, name, name_list)
|
||||
|
||||
return name_list
|
||||
|
||||
def save_invoice(e, si_doc, name, name_list):
|
||||
def save_invoice(doc, name, name_list):
|
||||
try:
|
||||
if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}):
|
||||
si_doc.docstatus = 0
|
||||
si_doc.flags.ignore_mandatory = True
|
||||
si_doc.due_date = si_doc.posting_date
|
||||
si_doc.insert()
|
||||
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()
|
||||
name_list.append(name)
|
||||
except Exception:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
|
||||
@@ -520,6 +520,24 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
};
|
||||
});
|
||||
},
|
||||
//When multiple companies are set up. in case company name is changed set default company address
|
||||
company:function(frm){
|
||||
if (frm.doc.company)
|
||||
{
|
||||
frappe.call({
|
||||
method:"frappe.contacts.doctype.address.address.get_default_address",
|
||||
args:{ doctype:'Company',name:frm.doc.company},
|
||||
callback: function(r){
|
||||
if (r.message){
|
||||
frm.set_value("company_address",r.message)
|
||||
}
|
||||
else {
|
||||
frm.set_value("company_address","")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
project: function(frm){
|
||||
frm.call({
|
||||
|
||||
@@ -4273,414 +4273,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"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_depends_on": "is_recurring",
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.docstatus<2 && !doc.__islocal",
|
||||
"fieldname": "recurring_invoice",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Recurring Invoice",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "fa fa-time",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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_break11",
|
||||
"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": "Settings",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.docstatus<2",
|
||||
"description": "",
|
||||
"fieldname": "is_recurring",
|
||||
"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": "Is Recurring",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "is_recurring",
|
||||
"description": "",
|
||||
"fieldname": "recurring_id",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Reference Document",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Sales Invoice",
|
||||
"permlevel": 0,
|
||||
"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": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "recurring_type",
|
||||
"fieldtype": "Select",
|
||||
"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": "Frequency",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "\nMonthly\nQuarterly\nHalf-yearly\nYearly",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "repeat_on_day_of_month",
|
||||
"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": "Repeat on Day of Month",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "end_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": "End Date",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"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": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "notify_by_email",
|
||||
"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": "Notify by email",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring && doc.notify_by_email && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "notification_email_address",
|
||||
"fieldtype": "Code",
|
||||
"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": "Notification Email Address",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Email",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring && doc.notify_by_email && doc.recurring_id === doc.name",
|
||||
"fieldname": "recurring_print_format",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Recurring Print Format",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Print Format",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"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_break12",
|
||||
"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": "This Document",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "is_recurring",
|
||||
"depends_on": "",
|
||||
"description": "",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
@@ -4711,7 +4304,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "is_recurring",
|
||||
"depends_on": "",
|
||||
"description": "",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
@@ -4742,10 +4335,8 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "is_recurring",
|
||||
"description": "",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"fieldname": "column_break_140",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@@ -4753,11 +4344,11 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Next Date",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
@@ -4767,6 +4358,37 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"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,
|
||||
@@ -4811,7 +4433,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-09-19 11:23:08.675028",
|
||||
"modified": "2017-10-24 12:46:48.331723",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
||||
@@ -27,8 +27,8 @@ form_grid_templates = {
|
||||
}
|
||||
|
||||
class SalesInvoice(SellingController):
|
||||
def __init__(self, arg1, arg2=None):
|
||||
super(SalesInvoice, self).__init__(arg1, arg2)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SalesInvoice, self).__init__(*args, **kwargs)
|
||||
self.status_updater = [{
|
||||
'source_dt': 'Sales Invoice Item',
|
||||
'target_field': 'billed_amt',
|
||||
@@ -70,6 +70,7 @@ class SalesInvoice(SellingController):
|
||||
self.clear_unallocated_advances("Sales Invoice Advance", "advances")
|
||||
self.add_remarks()
|
||||
self.validate_write_off_account()
|
||||
self.validate_duplicate_offline_pos_entry()
|
||||
self.validate_account_for_change_amount()
|
||||
self.validate_fixed_asset()
|
||||
self.set_income_account_for_fixed_assets()
|
||||
@@ -462,6 +463,12 @@ class SalesInvoice(SellingController):
|
||||
if flt(self.write_off_amount) and not self.write_off_account:
|
||||
msgprint(_("Please enter Write Off Account"), raise_exception=1)
|
||||
|
||||
def validate_duplicate_offline_pos_entry(self):
|
||||
if self.is_pos and self.offline_pos_name \
|
||||
and frappe.db.get_value('Sales Invoice',
|
||||
{'offline_pos_name': self.offline_pos_name, 'docstatus': 1}):
|
||||
frappe.throw(_("Duplicate offline pos sales invoice {0}").format(self.offline_pos_name))
|
||||
|
||||
def validate_account_for_change_amount(self):
|
||||
if flt(self.change_amount) and not self.account_for_change_amount:
|
||||
msgprint(_("Please enter Account for Change Amount"), raise_exception=1)
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest, copy
|
||||
from frappe.utils import nowdate, add_days, flt
|
||||
import unittest, copy, time
|
||||
from frappe.utils import nowdate, add_days, flt, cint
|
||||
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,6 +665,47 @@ 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`
|
||||
@@ -1084,7 +1125,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.insert()
|
||||
si.save()
|
||||
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):
|
||||
|
||||
@@ -699,7 +699,7 @@
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "2",
|
||||
"precision": "",
|
||||
"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-07-17 17:54:48.246507",
|
||||
"modified": "2017-09-27 08:31:37.827893",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Item",
|
||||
|
||||
@@ -3,10 +3,17 @@
|
||||
|
||||
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: {
|
||||
"docstatus": 1
|
||||
"docstatus": 1,
|
||||
"subscription": ''
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -135,66 +135,6 @@
|
||||
"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,
|
||||
@@ -286,12 +226,12 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "next_schedule_date",
|
||||
"fieldtype": "Date",
|
||||
"fieldname": "submit_on_creation",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@@ -299,14 +239,44 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Next Schedule Date",
|
||||
"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",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
@@ -320,7 +290,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "frequency_detail",
|
||||
"fieldname": "section_break_10",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@@ -329,7 +299,6 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@@ -381,7 +350,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_12",
|
||||
"fieldname": "column_break_13",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@@ -437,10 +406,40 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 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,
|
||||
"columns": 0,
|
||||
"fieldname": "notification",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -495,6 +494,38 @@
|
||||
"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,
|
||||
@@ -593,6 +624,69 @@
|
||||
"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,
|
||||
@@ -690,7 +784,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-09-14 12:09:38.471458",
|
||||
"modified": "2017-10-23 18:28:08.966403",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Subscription",
|
||||
@@ -700,7 +794,7 @@
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
@@ -720,7 +814,7 @@
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
@@ -740,7 +834,7 @@
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
|
||||
@@ -7,21 +7,28 @@ 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
|
||||
from frappe.utils import cstr, getdate, split_emails, add_days, today, get_last_day, get_first_day
|
||||
from frappe.model.document import Document
|
||||
|
||||
month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12}
|
||||
class Subscription(Document):
|
||||
def validate(self):
|
||||
self.update_status()
|
||||
self.validate_reference_doctype()
|
||||
self.validate_dates()
|
||||
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()
|
||||
if not self.next_schedule_date:
|
||||
self.next_schedule_date = get_next_schedule_date(self.start_date,
|
||||
self.frequency, self.repeat_on_day)
|
||||
|
||||
def on_submit(self):
|
||||
self.update_subscription_id()
|
||||
@@ -30,6 +37,18 @@ class Subscription(Document):
|
||||
self.validate_dates()
|
||||
self.set_next_schedule_date()
|
||||
|
||||
def before_cancel(self):
|
||||
self.unlink_subscription_id()
|
||||
self.next_schedule_date = None
|
||||
|
||||
def unlink_subscription_id(self):
|
||||
frappe.db.sql("update `tab{0}` set subscription = null where subscription=%s"
|
||||
.format(self.reference_doctype), self.name)
|
||||
|
||||
def validate_reference_doctype(self):
|
||||
if not frappe.get_meta(self.reference_doctype).has_field('subscription'):
|
||||
frappe.throw(_("Add custom field Subscription in the doctype {0}").format(self.reference_doctype))
|
||||
|
||||
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"))
|
||||
@@ -61,15 +80,11 @@ class Subscription(Document):
|
||||
frappe.throw(_("'Recipients' not specified"))
|
||||
|
||||
def set_next_schedule_date(self):
|
||||
self.next_schedule_date = get_next_schedule_date(self.start_date,
|
||||
self.frequency, self.repeat_on_day)
|
||||
if self.repeat_on_day:
|
||||
self.next_schedule_date = get_next_date(self.next_schedule_date, 0, self.repeat_on_day)
|
||||
|
||||
def update_subscription_id(self):
|
||||
doc = frappe.get_doc(self.reference_doctype, self.reference_document)
|
||||
if not doc.meta.get_field('subscription'):
|
||||
frappe.throw(_("Add custom field Subscription Id in the doctype {0}").format(self.reference_doctype))
|
||||
|
||||
doc.db_set('subscription', self.name)
|
||||
frappe.db.set_value(self.reference_doctype, self.reference_document, "subscription", self.name)
|
||||
|
||||
def update_status(self, status=None):
|
||||
self.status = {
|
||||
@@ -114,19 +129,19 @@ def create_documents(data, schedule_date):
|
||||
doc = make_new_document(data, schedule_date)
|
||||
if data.notify_by_email and data.recipients:
|
||||
print_format = data.print_format or "Standard"
|
||||
send_notification(doc, print_format, data.recipients)
|
||||
send_notification(doc, data, print_format=print_format)
|
||||
|
||||
frappe.db.commit()
|
||||
except Exception:
|
||||
frappe.db.rollback()
|
||||
frappe.db.begin()
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
disabled_subscription(data)
|
||||
disable_subscription(data)
|
||||
frappe.db.commit()
|
||||
if data.reference_document and not frappe.flags.in_test:
|
||||
notify_error_to_user(data)
|
||||
|
||||
def disabled_subscription(data):
|
||||
def disable_subscription(data):
|
||||
subscription = frappe.get_doc('Subscription', data.name)
|
||||
subscription.db_set('disabled', 1)
|
||||
|
||||
@@ -160,28 +175,79 @@ 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)
|
||||
|
||||
new_document.run_method("on_recurring", reference_doc=reference_doc, subscription_doc=args)
|
||||
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))
|
||||
|
||||
for data in new_document.meta.fields:
|
||||
if data.fieldtype == 'Date' and data.reqd:
|
||||
new_document.set(data.fieldname, schedule_date)
|
||||
|
||||
set_subscription_period(args, mcount, new_document)
|
||||
|
||||
new_document.run_method("on_recurring", reference_doc=reference_doc, subscription_doc=args)
|
||||
|
||||
def set_subscription_period(args, mcount, new_document):
|
||||
if new_document.meta.get_field('from_date') and new_document.meta.get_field('to_date'):
|
||||
last_ref_doc = frappe.db.sql("""
|
||||
select name, from_date, to_date
|
||||
from `tab{0}`
|
||||
where subscription=%s and docstatus < 2
|
||||
order by creation desc
|
||||
limit 1
|
||||
""".format(args.reference_doctype), args.name, as_dict=1)
|
||||
|
||||
if not last_ref_doc:
|
||||
return
|
||||
|
||||
from_date = get_next_date(last_ref_doc[0].from_date, mcount)
|
||||
|
||||
if (cstr(get_first_day(last_ref_doc[0].from_date)) == cstr(last_ref_doc[0].from_date)) and \
|
||||
(cstr(get_last_day(last_ref_doc[0].to_date)) == cstr(last_ref_doc[0].to_date)):
|
||||
to_date = get_last_day(get_next_date(last_ref_doc[0].to_date, mcount))
|
||||
else:
|
||||
to_date = get_next_date(last_ref_doc[0].to_date, mcount)
|
||||
|
||||
new_document.set('from_date', from_date)
|
||||
new_document.set('to_date', to_date)
|
||||
|
||||
def get_next_date(dt, mcount, day=None):
|
||||
dt = getdate(dt)
|
||||
dt += relativedelta(months=mcount, day=day)
|
||||
|
||||
return dt
|
||||
|
||||
def send_notification(new_rv, print_format='Standard', recipients=None):
|
||||
def send_notification(new_rv, subscription_doc, print_format='Standard'):
|
||||
"""Notify concerned persons about recurring document generation"""
|
||||
print_format = print_format
|
||||
|
||||
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)])
|
||||
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)
|
||||
|
||||
def notify_errors(doc, doctype, party, owner, name):
|
||||
recipients = get_system_managers(only_name=True)
|
||||
@@ -210,8 +276,11 @@ def assign_task_to_owner(name, msg, users):
|
||||
@frappe.whitelist()
|
||||
def make_subscription(doctype, docname):
|
||||
doc = frappe.new_doc('Subscription')
|
||||
|
||||
reference_doc = frappe.get_doc(doctype, docname)
|
||||
doc.reference_doctype = doctype
|
||||
doc.reference_document = docname
|
||||
doc.start_date = reference_doc.get('posting_date') or reference_doc.get('transaction_date')
|
||||
return doc
|
||||
|
||||
@frappe.whitelist()
|
||||
@@ -225,4 +294,20 @@ def stop_resume_subscription(subscription, status):
|
||||
doc.update_status(status)
|
||||
doc.save()
|
||||
|
||||
return doc.status
|
||||
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
|
||||
})
|
||||
@@ -8,6 +8,7 @@ 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
|
||||
@@ -136,7 +137,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 = _("All Customer Groups")
|
||||
if not value: value = get_root_of("Customer Group")
|
||||
customer_group_condition = get_customer_group_condition(value)
|
||||
conditions.append("ifnull({0}, '') in ('', {1})".format(key, customer_group_condition))
|
||||
else:
|
||||
|
||||
@@ -84,6 +84,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
this.get_data_from_server(function () {
|
||||
me.make_control();
|
||||
me.create_new();
|
||||
me.make();
|
||||
});
|
||||
},
|
||||
|
||||
@@ -113,6 +114,7 @@ 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()
|
||||
});
|
||||
|
||||
@@ -381,7 +383,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
},
|
||||
|
||||
setup: function () {
|
||||
this.make();
|
||||
this.set_primary_action();
|
||||
this.party_field.$input.attr('disabled', false);
|
||||
if(this.selected_row) {
|
||||
@@ -1340,6 +1341,12 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
|
||||
this.wrapper.find('input.discount-percentage').on("change", function () {
|
||||
me.frm.doc.additional_discount_percentage = flt($(this).val(), precision("additional_discount_percentage"));
|
||||
|
||||
if(me.frm.doc.additional_discount_percentage && me.frm.doc.discount_amount) {
|
||||
// Reset discount amount
|
||||
me.frm.doc.discount_amount = 0;
|
||||
}
|
||||
|
||||
var total = me.frm.doc.grand_total
|
||||
|
||||
if (me.frm.doc.apply_discount_on == 'Net Total') {
|
||||
@@ -1347,15 +1354,15 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
}
|
||||
|
||||
me.frm.doc.discount_amount = flt(total * flt(me.frm.doc.additional_discount_percentage) / 100, precision("discount_amount"));
|
||||
me.wrapper.find('input.discount-amount').val(me.frm.doc.discount_amount)
|
||||
me.refresh();
|
||||
me.wrapper.find('input.discount-amount').val(me.frm.doc.discount_amount)
|
||||
});
|
||||
|
||||
this.wrapper.find('input.discount-amount').on("change", function () {
|
||||
me.frm.doc.discount_amount = flt($(this).val(), precision("discount_amount"));
|
||||
me.frm.doc.additional_discount_percentage = 0.0;
|
||||
me.wrapper.find('input.discount-percentage').val(0);
|
||||
me.refresh();
|
||||
me.wrapper.find('input.discount-percentage').val(0);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1516,6 +1523,8 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
var me = this;
|
||||
this.wrapper.find(".net-total").text(format_currency(me.frm.doc.total, me.frm.doc.currency));
|
||||
this.wrapper.find(".grand-total").text(format_currency(me.frm.doc.grand_total, me.frm.doc.currency));
|
||||
this.wrapper.find('input.discount-percentage').val(this.frm.doc.additional_discount_percentage);
|
||||
this.wrapper.find('input.discount-amount').val(this.frm.doc.discount_amount);
|
||||
},
|
||||
|
||||
set_primary_action: function () {
|
||||
@@ -1684,6 +1693,7 @@ 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)
|
||||
},
|
||||
@@ -1697,9 +1707,12 @@ 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,
|
||||
|
||||
@@ -68,16 +68,18 @@ 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)
|
||||
out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field]))
|
||||
if doctype:
|
||||
out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field]))
|
||||
|
||||
# address display
|
||||
out.address_display = get_address_display(out[billing_address_field])
|
||||
|
||||
# shipping address
|
||||
if party_type in ["Customer", "Lead"]:
|
||||
out.shipping_address_name = get_default_address(party_type, party.name, 'is_shipping_address')
|
||||
out.shipping_address_name = get_party_shipping_address(party_type, party.name)
|
||||
out.shipping_address = get_address_display(out["shipping_address_name"])
|
||||
out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name))
|
||||
if doctype:
|
||||
out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name))
|
||||
|
||||
if doctype and doctype in ['Delivery Note', 'Sales Invoice']:
|
||||
out.update(get_company_address(company))
|
||||
@@ -320,11 +322,15 @@ def set_taxes(party, party_type, posting_date, company, customer_group=None, sup
|
||||
from erpnext.accounts.doctype.tax_rule.tax_rule import get_tax_template, get_party_details
|
||||
args = {
|
||||
party_type.lower(): party,
|
||||
"customer_group": customer_group,
|
||||
"supplier_type": supplier_type,
|
||||
"company": company
|
||||
}
|
||||
|
||||
if customer_group:
|
||||
args['customer_group'] = customer_group
|
||||
|
||||
if supplier_type:
|
||||
args['supplier_type'] = supplier_type
|
||||
|
||||
if billing_address or shipping_address:
|
||||
args.update(get_party_details(party, party_type, {"billing_address": billing_address, \
|
||||
"shipping_address": shipping_address }))
|
||||
@@ -412,3 +418,32 @@ def get_dashboard_info(party_type, party):
|
||||
info["total_unpaid"] = -1 * info["total_unpaid"]
|
||||
|
||||
return info
|
||||
|
||||
|
||||
def get_party_shipping_address(doctype, name):
|
||||
"""
|
||||
Returns an Address name (best guess) for the given doctype and name for which `address_type == 'Shipping'` is true.
|
||||
and/or `is_shipping_address = 1`.
|
||||
|
||||
It returns an empty string if there is no matching record.
|
||||
|
||||
:param doctype: Party Doctype
|
||||
:param name: Party name
|
||||
:return: String
|
||||
"""
|
||||
out = frappe.db.sql(
|
||||
'SELECT dl.parent '
|
||||
'from `tabDynamic Link` dl join `tabAddress` ta on dl.parent=ta.name '
|
||||
'where '
|
||||
'dl.link_doctype=%s '
|
||||
'and dl.link_name=%s '
|
||||
'and dl.parenttype="Address" '
|
||||
'and '
|
||||
'(ta.address_type="Shipping" or ta.is_shipping_address=1) '
|
||||
'order by ta.is_shipping_address desc, ta.address_type desc limit 1',
|
||||
(doctype, name)
|
||||
)
|
||||
if out:
|
||||
return out[0][0]
|
||||
else:
|
||||
return ''
|
||||
|
||||
@@ -83,7 +83,7 @@ frappe.query_reports["General Ledger"] = {
|
||||
return;
|
||||
}
|
||||
|
||||
var fieldname = frappe.model.scrub(party_type)+"_name";
|
||||
var fieldname = party_type.toLowerCase() + "_name";
|
||||
frappe.db.get_value(party_type, party, fieldname, function(value) {
|
||||
frappe.query_report_filters_by_name.party_name.set_value(value[fieldname]);
|
||||
});
|
||||
|
||||
@@ -107,6 +107,8 @@ 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
|
||||
@@ -150,7 +152,6 @@ 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]):
|
||||
|
||||
0
erpnext/accounts/test/__init__.py
Normal file
0
erpnext/accounts/test/__init__.py
Normal file
84
erpnext/accounts/test/test_utils.py
Normal file
84
erpnext/accounts/test/test_utils.py
Normal file
@@ -0,0 +1,84 @@
|
||||
import unittest
|
||||
from erpnext.accounts.party import get_party_shipping_address
|
||||
from frappe.test_runner import make_test_objects
|
||||
|
||||
|
||||
class TestUtils(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestUtils, cls).setUpClass()
|
||||
make_test_objects('Address', ADDRESS_RECORDS)
|
||||
|
||||
def test_get_party_shipping_address(self):
|
||||
address = get_party_shipping_address('Customer', '_Test Customer 1')
|
||||
self.assertEqual(address, '_Test Billing Address 2 Title-Billing')
|
||||
|
||||
def test_get_party_shipping_address2(self):
|
||||
address = get_party_shipping_address('Customer', '_Test Customer 2')
|
||||
self.assertEqual(address, '_Test Shipping Address 2 Title-Shipping')
|
||||
|
||||
|
||||
ADDRESS_RECORDS = [
|
||||
{
|
||||
"doctype": "Address",
|
||||
"address_type": "Billing",
|
||||
"address_line1": "Address line 1",
|
||||
"address_title": "_Test Billing Address Title",
|
||||
"city": "Lagos",
|
||||
"country": "Nigeria",
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": "Customer",
|
||||
"link_name": "_Test Customer 2",
|
||||
"doctype": "Dynamic Link"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"doctype": "Address",
|
||||
"address_type": "Shipping",
|
||||
"address_line1": "Address line 2",
|
||||
"address_title": "_Test Shipping Address 1 Title",
|
||||
"city": "Lagos",
|
||||
"country": "Nigeria",
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": "Customer",
|
||||
"link_name": "_Test Customer 2",
|
||||
"doctype": "Dynamic Link"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"doctype": "Address",
|
||||
"address_type": "Shipping",
|
||||
"address_line1": "Address line 3",
|
||||
"address_title": "_Test Shipping Address 2 Title",
|
||||
"city": "Lagos",
|
||||
"country": "Nigeria",
|
||||
"is_shipping_address": "1",
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": "Customer",
|
||||
"link_name": "_Test Customer 2",
|
||||
"doctype": "Dynamic Link"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"doctype": "Address",
|
||||
"address_type": "Billing",
|
||||
"address_line1": "Address line 4",
|
||||
"address_title": "_Test Billing Address 2 Title",
|
||||
"city": "Lagos",
|
||||
"country": "Nigeria",
|
||||
"is_shipping_address": "1",
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": "Customer",
|
||||
"link_name": "_Test Customer 1",
|
||||
"doctype": "Dynamic Link"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -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, paying_party=False):
|
||||
def get_outstanding_invoices(party_type, party, account, condition=None):
|
||||
outstanding_invoices = []
|
||||
precision = frappe.get_precision("Sales Invoice", "outstanding_amount")
|
||||
|
||||
if party_type=="Customer" or paying_party:
|
||||
if party_type=="Customer":
|
||||
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:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
@@ -24,6 +25,18 @@ frappe.ui.form.on("Purchase Order", {
|
||||
},
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Purchase Order Item", {
|
||||
item_code: function(frm) {
|
||||
frappe.call({
|
||||
method: "get_last_purchase_rate",
|
||||
doc: frm.doc,
|
||||
callback: function(r, rt) {
|
||||
frm.trigger('calculate_taxes_and_totals');
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({
|
||||
refresh: function(doc, cdt, cdn) {
|
||||
var me = this;
|
||||
@@ -214,17 +227,6 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
|
||||
delivered_by_supplier: function(){
|
||||
cur_frm.cscript.update_status('Deliver', 'Delivered')
|
||||
},
|
||||
|
||||
get_last_purchase_rate: function() {
|
||||
frappe.call({
|
||||
"method": "get_last_purchase_rate",
|
||||
"doc": cur_frm.doc,
|
||||
callback: function(r, rt) {
|
||||
cur_frm.dirty();
|
||||
cur_frm.cscript.calculate_taxes_and_totals();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -1206,37 +1206,6 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.docstatus===0 && (doc.items && doc.items.length)",
|
||||
"fieldname": "get_last_purchase_rate",
|
||||
"fieldtype": "Button",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Get last purchase rate",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@@ -2948,418 +2917,13 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"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_depends_on": "is_recurring",
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.docstatus<2 && !doc.__islocal",
|
||||
"fieldname": "recurring_order",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Recurring Order",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "fa fa-time",
|
||||
"permlevel": 0,
|
||||
"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",
|
||||
"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": "Settings",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"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,
|
||||
"depends_on": "eval:doc.docstatus<2",
|
||||
"description": "",
|
||||
"fieldname": "is_recurring",
|
||||
"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": "Is Recurring",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "is_recurring",
|
||||
"description": "",
|
||||
"fieldname": "recurring_id",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Reference Document",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Purchase Order",
|
||||
"permlevel": 0,
|
||||
"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": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "recurring_type",
|
||||
"fieldtype": "Select",
|
||||
"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": "Frequency",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Monthly\nQuarterly\nHalf-yearly\nYearly",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "repeat_on_day_of_month",
|
||||
"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": "Repeat on Day of Month",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "end_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": "End Date",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"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": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "notify_by_email",
|
||||
"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": "Notify by email",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring && doc.notify_by_email && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "notification_email_address",
|
||||
"fieldtype": "Code",
|
||||
"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": "Notification Email Address",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Email",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring && doc.notify_by_email && doc.recurring_id === doc.name",
|
||||
"fieldname": "recurring_print_format",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Recurring Print Format",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Print Format",
|
||||
"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_break83",
|
||||
"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": "This Document",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"depends_on": "is_recurring",
|
||||
"depends_on": "",
|
||||
"description": "",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
@@ -3390,7 +2954,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "is_recurring",
|
||||
"depends_on": "",
|
||||
"description": "",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
@@ -3421,10 +2985,8 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "is_recurring",
|
||||
"description": "",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"fieldname": "column_break_97",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@@ -3432,11 +2994,11 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Next Date",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
@@ -3445,6 +3007,37 @@
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
@@ -3458,7 +3051,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-09-19 11:22:30.190589",
|
||||
"modified": "2017-10-24 12:52:11.272306",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
||||
@@ -20,8 +20,8 @@ form_grid_templates = {
|
||||
}
|
||||
|
||||
class PurchaseOrder(BuyingController):
|
||||
def __init__(self, arg1, arg2=None):
|
||||
super(PurchaseOrder, self).__init__(arg1, arg2)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PurchaseOrder, self).__init__(*args, **kwargs)
|
||||
self.status_updater = [{
|
||||
'source_dt': 'Purchase Order Item',
|
||||
'target_dt': 'Material Request Item',
|
||||
@@ -116,14 +116,13 @@ class PurchaseOrder(BuyingController):
|
||||
d.discount_percentage = last_purchase_details['discount_percentage']
|
||||
d.base_rate = last_purchase_details['base_rate'] * (flt(d.conversion_factor) or 1.0)
|
||||
d.price_list_rate = d.base_price_list_rate / conversion_rate
|
||||
d.rate = d.base_rate / conversion_rate
|
||||
d.last_purchase_rate = d.base_rate / conversion_rate
|
||||
else:
|
||||
msgprint(_("Last purchase rate not found"))
|
||||
|
||||
item_last_purchase_rate = frappe.db.get_value("Item", d.item_code, "last_purchase_rate")
|
||||
if item_last_purchase_rate:
|
||||
d.base_price_list_rate = d.base_rate = d.price_list_rate \
|
||||
= d.rate = item_last_purchase_rate
|
||||
= d.last_purchase_rate = item_last_purchase_rate
|
||||
|
||||
# Check for Closed status
|
||||
def check_for_closed_status(self):
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
QUnit.module('Buying');
|
||||
|
||||
QUnit.test("test: purchase order with last purchase rate", function(assert) {
|
||||
assert.expect(5);
|
||||
let done = assert.async();
|
||||
|
||||
frappe.run_serially([
|
||||
() => {
|
||||
return frappe.tests.make('Purchase Order', [
|
||||
{supplier: 'Test Supplier'},
|
||||
{is_subcontracted: 'No'},
|
||||
{currency: 'INR'},
|
||||
{items: [
|
||||
[
|
||||
{"item_code": 'Test Product 4'},
|
||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
||||
{"qty": 1},
|
||||
{"rate": 800},
|
||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
||||
],
|
||||
[
|
||||
{"item_code": 'Test Product 1'},
|
||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
||||
{"qty": 1},
|
||||
{"rate": 400},
|
||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
||||
]
|
||||
]}
|
||||
]);
|
||||
},
|
||||
|
||||
() => {
|
||||
// Get item details
|
||||
assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item 1 name correct");
|
||||
assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Item 2 name correct");
|
||||
},
|
||||
|
||||
() => frappe.timeout(1),
|
||||
|
||||
() => frappe.tests.click_button('Submit'),
|
||||
() => frappe.tests.click_button('Yes'),
|
||||
() => frappe.timeout(3),
|
||||
|
||||
() => frappe.tests.click_button('Close'),
|
||||
() => frappe.timeout(1),
|
||||
|
||||
() => {
|
||||
return frappe.tests.make('Purchase Order', [
|
||||
{supplier: 'Test Supplier'},
|
||||
{is_subcontracted: 'No'},
|
||||
{currency: 'INR'},
|
||||
{items: [
|
||||
[
|
||||
{"item_code": 'Test Product 4'},
|
||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
||||
{"qty": 1},
|
||||
{"rate": 600},
|
||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
||||
],
|
||||
[
|
||||
{"item_code": 'Test Product 1'},
|
||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
||||
{"qty": 1},
|
||||
{"rate": 200},
|
||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
||||
]
|
||||
]}
|
||||
]);
|
||||
},
|
||||
|
||||
() => frappe.timeout(2),
|
||||
|
||||
// Get the last purchase rate of items
|
||||
() => {
|
||||
assert.ok(cur_frm.doc.items[0].last_purchase_rate == 800, "Last purchase rate of item 1 correct");
|
||||
},
|
||||
() => {
|
||||
assert.ok(cur_frm.doc.items[1].last_purchase_rate == 400, "Last purchase rate of item 2 correct");
|
||||
},
|
||||
|
||||
() => frappe.tests.click_button('Submit'),
|
||||
() => frappe.tests.click_button('Yes'),
|
||||
() => frappe.timeout(3),
|
||||
|
||||
() => frappe.tests.click_button('Close'),
|
||||
|
||||
() => frappe.timeout(1),
|
||||
|
||||
() => {
|
||||
assert.ok(cur_frm.doc.status == 'To Receive and Bill', "Submitted successfully");
|
||||
},
|
||||
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
@@ -655,6 +655,37 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "last_purchase_rate",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Last Purchase Rate",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@@ -1714,7 +1745,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-08-02 22:15:47.411235",
|
||||
"modified": "2017-09-22 16:47:08.783546",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order Item",
|
||||
|
||||
@@ -254,6 +254,21 @@ 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
|
||||
|
||||
@@ -816,7 +816,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-07-21 14:06:46.309322",
|
||||
"modified": "2017-10-17 17:27:06.281494",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Request for Quotation",
|
||||
@@ -903,26 +903,6 @@
|
||||
"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,
|
||||
|
||||
@@ -2,29 +2,29 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.query_reports["Quoted Item Comparison"] = {
|
||||
"filters": [
|
||||
filters: [
|
||||
{
|
||||
"fieldname": "supplier_quotation",
|
||||
"label": __("Supplier Quotation"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier Quotation",
|
||||
"default": "",
|
||||
"get_query": function () {
|
||||
fieldtype: "Link",
|
||||
label: __("Supplier Quotation"),
|
||||
options: "Supplier Quotation",
|
||||
fieldname: "supplier_quotation",
|
||||
default: "",
|
||||
get_query: () => {
|
||||
return { filters: { "docstatus": ["<", 2] } }
|
||||
}
|
||||
},
|
||||
{
|
||||
"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();
|
||||
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();
|
||||
if (quote != "") {
|
||||
return {
|
||||
query: "erpnext.buying.doctype.quality_inspection.quality_inspection.item_query",
|
||||
query: "erpnext.stock.doctype.quality_inspection.quality_inspection.item_query",
|
||||
filters: {
|
||||
"from": "Supplier Quotation Item",
|
||||
"parent": quote
|
||||
@@ -39,47 +39,50 @@ frappe.query_reports["Quoted Item Comparison"] = {
|
||||
}
|
||||
}
|
||||
],
|
||||
onload: function (report) {
|
||||
onload: (report) => {
|
||||
// Create a button for setting the default supplier
|
||||
report.page.add_inner_button(__("Select Default Supplier"), function () {
|
||||
|
||||
var reporter = frappe.query_reports["Quoted Item Comparison"];
|
||||
report.page.add_inner_button(__("Select Default Supplier"), () => {
|
||||
let 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": function (report) {
|
||||
make_default_supplier_dialog: (report) => {
|
||||
// Get the name of the item to change
|
||||
var filters = report.get_values();
|
||||
var item_code = filters.item;
|
||||
if(!report.data) return;
|
||||
|
||||
let filters = report.get_values();
|
||||
let item_code = filters.item;
|
||||
|
||||
// Get a list of the suppliers (with a blank as well) for the user to select
|
||||
var select_options = "";
|
||||
for (let supplier of report.data) {
|
||||
select_options += supplier.supplier_name + '\n'
|
||||
}
|
||||
let suppliers = $.map(report.data, (row, idx)=>{ return row.supplier_name })
|
||||
|
||||
// Create a dialog window for the user to pick their supplier
|
||||
var d = new frappe.ui.Dialog({
|
||||
let dialog = new frappe.ui.Dialog({
|
||||
title: __('Select Default Supplier'),
|
||||
fields: [
|
||||
{ fieldname: 'supplier', fieldtype: 'Select', label: 'Supplier', reqd: 1, options: select_options },
|
||||
{ fieldname: 'ok_button', fieldtype: 'Button', label: 'Set Default Supplier' },
|
||||
{
|
||||
reqd: 1,
|
||||
label: 'Supplier',
|
||||
fieldtype: 'Link',
|
||||
options: 'Supplier',
|
||||
fieldname: 'supplier',
|
||||
get_query: () => {
|
||||
return {
|
||||
filters: {
|
||||
'name': ['in', suppliers]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// 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();
|
||||
|
||||
dialog.set_primary_action("Set Default Supplier", () => {
|
||||
let values = dialog.get_values();
|
||||
if(values) {
|
||||
// Set the default_supplier field of the appropriate Item to the selected supplier
|
||||
frappe.call({
|
||||
method: "frappe.client.set_value",
|
||||
@@ -87,17 +90,17 @@ frappe.query_reports["Quoted Item Comparison"] = {
|
||||
doctype: "Item",
|
||||
name: item_code,
|
||||
fieldname: "default_supplier",
|
||||
value: v.supplier,
|
||||
value: values.supplier,
|
||||
},
|
||||
callback: function (r) {
|
||||
$(btn).done_working();
|
||||
freeze: true,
|
||||
callback: (r) => {
|
||||
frappe.msgprint("Successfully Set Supplier");
|
||||
report.dialog.hide();
|
||||
dialog.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
report.dialog = d;
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,53 +8,55 @@ 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 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({
|
||||
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({
|
||||
"supplier": splr.supplier,
|
||||
"qty": root.qty,
|
||||
"parent": root.parent,
|
||||
"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
|
||||
"rate": root.rate
|
||||
})
|
||||
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)
|
||||
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)
|
||||
|
||||
return out
|
||||
|
||||
@@ -62,7 +64,8 @@ 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({
|
||||
@@ -98,4 +101,4 @@ def get_columns(qty_list):
|
||||
"width": 90
|
||||
})
|
||||
|
||||
return columns
|
||||
return columns
|
||||
@@ -1,4 +1,4 @@
|
||||
#### POS
|
||||
- POS - Online & Offline
|
||||
- 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,3 +6,25 @@
|
||||
#### 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
|
||||
@@ -315,11 +315,16 @@ 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": _("Rules to calculate shipping amount for a sale")
|
||||
"description": _("Setup default values for POS Invoices")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
|
||||
@@ -268,5 +268,13 @@ def get_data():
|
||||
"icon": "octicon octicon-plus",
|
||||
"type": "module",
|
||||
"label": _("Healthcare")
|
||||
}
|
||||
},
|
||||
{
|
||||
"module_name": "Data Import Tool",
|
||||
"color": "#7f8c8d",
|
||||
"icon": "octicon octicon-circuit-board",
|
||||
"type": "page",
|
||||
"link": "data-import-tool",
|
||||
"label": _("Data Import Tool")
|
||||
},
|
||||
]
|
||||
|
||||
@@ -123,6 +123,12 @@ def get_data():
|
||||
"is_query_report": True,
|
||||
"name": "BOM Search",
|
||||
"doctype": "BOM"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "BOM Stock Report",
|
||||
"doctype": "BOM"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -105,6 +105,11 @@ def get_data():
|
||||
"name": "Pricing Rule",
|
||||
"description": _("Rules for applying pricing and discount.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Item Variant Settings",
|
||||
"description": _("Item Variant Settings."),
|
||||
},
|
||||
|
||||
]
|
||||
},
|
||||
|
||||
@@ -15,8 +15,8 @@ from erpnext.exceptions import InvalidCurrency
|
||||
force_item_fields = ("item_group", "barcode", "brand", "stock_uom")
|
||||
|
||||
class AccountsController(TransactionBase):
|
||||
def __init__(self, arg1, arg2=None):
|
||||
super(AccountsController, self).__init__(arg1, arg2)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AccountsController, self).__init__(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def company_currency(self):
|
||||
@@ -187,9 +187,6 @@ 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"))
|
||||
|
||||
@@ -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))
|
||||
self.update_if_missing(get_party_details(self.supplier, party_type="Supplier", ignore_permissions=self.flags.ignore_permissions, doctype=self.doctype, company=self.company))
|
||||
|
||||
self.set_missing_item_details(for_validate)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import cstr, flt
|
||||
import json
|
||||
import json, copy
|
||||
|
||||
class ItemVariantExistsError(frappe.ValidationError): pass
|
||||
class InvalidItemAttributeValueError(frappe.ValidationError): pass
|
||||
@@ -174,28 +174,43 @@ def copy_attributes_to_variant(item, variant):
|
||||
|
||||
# copy non no-copy fields
|
||||
|
||||
exclude_fields = ["item_code", "item_name", "show_in_website"]
|
||||
exclude_fields = ["naming_series", "item_code", "item_name", "show_in_website",
|
||||
"show_variant_in_website", "opening_stock", "variant_of", "valuation_rate"]
|
||||
|
||||
if item.variant_based_on=='Manufacturer':
|
||||
# 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.fieldtype == 'Table' or field.fieldtype not in no_value_fields) \
|
||||
and (not field.no_copy) and field.fieldname not in exclude_fields:
|
||||
if (field.reqd or field.fieldname in allow_fields) and field.fieldname not in exclude_fields:
|
||||
if variant.get(field.fieldname) != item.get(field.fieldname):
|
||||
variant.set(field.fieldname, item.get(field.fieldname))
|
||||
if field.fieldtype == "Table":
|
||||
variant.set(field.fieldname, [])
|
||||
for d in item.get(field.fieldname):
|
||||
row = copy.deepcopy(d)
|
||||
if row.get("name"):
|
||||
row.name = None
|
||||
variant.append(field.fieldname, row)
|
||||
else:
|
||||
variant.set(field.fieldname, item.get(field.fieldname))
|
||||
|
||||
variant.variant_of = item.name
|
||||
variant.has_variants = 0
|
||||
if not variant.description:
|
||||
variant.description = ''
|
||||
variant.description = ""
|
||||
|
||||
if item.variant_based_on=='Item Attribute':
|
||||
if variant.attributes:
|
||||
variant.description += "\n"
|
||||
attributes_description = ""
|
||||
for d in variant.attributes:
|
||||
variant.description += "<p>" + d.attribute + ": " + cstr(d.attribute_value) + "</p>"
|
||||
attributes_description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"
|
||||
|
||||
if attributes_description not in variant.description:
|
||||
variant.description += attributes_description
|
||||
|
||||
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"""
|
||||
|
||||
@@ -49,7 +49,8 @@ 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)
|
||||
ignore_permissions=self.flags.ignore_permissions,
|
||||
doctype=self.doctype, company=self.company)
|
||||
if not self.meta.get_field("sales_team"):
|
||||
party_details.pop("sales_team")
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ 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
|
||||
@@ -54,5 +55,7 @@ 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"), [])
|
||||
|
||||
@@ -42,10 +42,28 @@ 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)
|
||||
@@ -245,6 +263,27 @@ 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, {
|
||||
@@ -284,4 +323,4 @@ def auto_close_opportunity():
|
||||
doc.status = "Closed"
|
||||
doc.flags.ignore_permissions = True
|
||||
doc.flags.ignore_mandatory = True
|
||||
doc.save()
|
||||
doc.save()
|
||||
|
||||
@@ -4,6 +4,7 @@ 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
|
||||
|
||||
@@ -25,12 +26,45 @@ 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": "Customer" or args.enquiry_from,
|
||||
"enquiry_from": args.enquiry_from or "Customer",
|
||||
"enquiry_type": "Sales",
|
||||
"with_items": args.with_items or 0,
|
||||
"transaction_date": today()
|
||||
|
||||
@@ -278,5 +278,16 @@
|
||||
"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"
|
||||
}
|
||||
]
|
||||
@@ -16,6 +16,7 @@ def setup(domain):
|
||||
setup_user()
|
||||
setup_employee()
|
||||
setup_user_roles()
|
||||
setup_role_permissions()
|
||||
|
||||
employees = frappe.get_all('Employee', fields=['name', 'date_of_joining'])
|
||||
|
||||
@@ -91,7 +92,8 @@ def setup_fiscal_year():
|
||||
pass
|
||||
|
||||
# set the last fiscal year (current year) as default
|
||||
fiscal_year.set_as_default()
|
||||
if fiscal_year:
|
||||
fiscal_year.set_as_default()
|
||||
|
||||
def setup_holiday_list():
|
||||
"""Setup Holiday List for the current year"""
|
||||
@@ -374,6 +376,22 @@ 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',
|
||||
|
||||
@@ -7,6 +7,7 @@ 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
|
||||
|
||||
@@ -59,7 +60,7 @@ def make_delivery_note():
|
||||
try:
|
||||
dn.submit()
|
||||
frappe.db.commit()
|
||||
except (NegativeStockError, SerialNoRequiredError, SerialNoQtyError):
|
||||
except (NegativeStockError, SerialNoRequiredError, SerialNoQtyError, UnableToSelectBatchError):
|
||||
frappe.db.rollback()
|
||||
|
||||
def make_stock_reconciliation():
|
||||
|
||||
BIN
erpnext/docs/assets/img/stock/item_variants_settings.png
Normal file
BIN
erpnext/docs/assets/img/stock/item_variants_settings.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
@@ -13,4 +13,8 @@
|
||||
|
||||
* Unlink Payment on Cancellation of Invoice: If checked, system will unlink the payment against the invoice. Otherwise, it will show the link error.
|
||||
|
||||
* 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}
|
||||
|
||||
@@ -48,4 +48,11 @@ When you make a new Variant, the system will prompt you to select a Manufacturer
|
||||
<img class='screenshot' alt='Setup Item Variant by Manufacturer'
|
||||
src='/docs/assets/img/stock/set-variant-by-mfg.png'>
|
||||
|
||||
The naming of the variant will be the name (ID) of the template Item with a number suffix. e.g. "ITEM000" will have variant "ITEM000-1"
|
||||
The naming of the variant will be the name (ID) of the template Item with a number suffix. e.g. "ITEM000" will have variant "ITEM000-1"
|
||||
|
||||
### Update Variants Based on Template
|
||||
To update the value in the variants items from the template item, select the respective fields first in the Item Variant Settings page. After that system will update the value of that fields in the variants if that values has been changed in the template item.
|
||||
|
||||
To set the fields Goto Stock > Item Variant Settings
|
||||
<img class='screenshot' alt='Item Variant Settings'
|
||||
src='/docs/assets/img/stock/item_variants_settings.png'>
|
||||
|
||||
@@ -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='{0}' where name='{1}'""").format(sales_invoice.name, consultation_id))
|
||||
frappe.db.sql("""update tabConsultation set invoice=%s where name=%s""", (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)
|
||||
|
||||
@@ -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='{0}' and cp.parent=ct.name and cp.test_created=0""").format(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))
|
||||
|
||||
@@ -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='{0}'""").format(patient), as_dict=1)
|
||||
patient_dict = frappe.db.sql("""select * from tabPatient where name=%s""", (patient), as_dict=1)
|
||||
if not patient_dict:
|
||||
frappe.throw("Patient not found")
|
||||
vital_sign = frappe.db.sql(_("""select * from `tabVital Signs` where patient='{0}' order by signs_date desc limit 1""").format(patient), as_dict=1)
|
||||
vital_sign = frappe.db.sql("""select * from `tabVital Signs` where patient=%s order by signs_date desc limit 1""", (patient), as_dict=1)
|
||||
|
||||
details = patient_dict[0]
|
||||
if vital_sign:
|
||||
|
||||
@@ -183,16 +183,20 @@ var btn_create_vital_signs = function (frm) {
|
||||
|
||||
var btn_update_status = function(frm, status){
|
||||
var doc = frm.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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
);
|
||||
};
|
||||
|
||||
var btn_invoice_consultation = function(frm){
|
||||
|
||||
@@ -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='{0}' where name='{1}'""").format(sales_invoice.name, appointment_id))
|
||||
frappe.db.sql("""update `tabPatient Appointment` set sales_invoice=%s where name=%s""", (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",
|
||||
|
||||
@@ -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 = '8.x.x-beta'
|
||||
develop_version = '9.x.x-develop'
|
||||
|
||||
error_report_email = "support@erpnext.com"
|
||||
|
||||
@@ -27,7 +27,6 @@ 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"
|
||||
@@ -48,7 +47,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", "Opportunity", "Issue"]
|
||||
email_append_to = ["Job Applicant", "Lead", "Opportunity", "Issue"]
|
||||
|
||||
calendars = ["Task", "Production Order", "Leave Application", "Sales Order", "Holiday List"]
|
||||
|
||||
@@ -81,7 +80,7 @@ website_route_rules = [
|
||||
{"from_route": "/supplier-quotations/<path:name>", "to_route": "order",
|
||||
"defaults": {
|
||||
"doctype": "Supplier Quotation",
|
||||
"parents": [{"label": _("Supplier Quotation"), "route": "quotations"}]
|
||||
"parents": [{"label": _("Supplier Quotation"), "route": "supplier-quotations"}]
|
||||
}
|
||||
},
|
||||
{"from_route": "/quotations", "to_route": "Quotation"},
|
||||
|
||||
@@ -149,7 +149,7 @@
|
||||
"in_filter": 0,
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"in_standard_filter": 1,
|
||||
"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-06-13 14:29:13.694009",
|
||||
"modified": "2017-10-04 11:42:02.495731",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Employee",
|
||||
|
||||
@@ -21,7 +21,7 @@ class JobOpening(WebsiteGenerator):
|
||||
self.route = frappe.scrub(self.job_title).replace('_', '-')
|
||||
|
||||
def get_context(self, context):
|
||||
context.parents = [{'name': 'jobs', 'title': _('All Jobs') }]
|
||||
context.parents = [{'route': 'jobs', 'title': _('All Jobs') }]
|
||||
|
||||
def get_list_context(context):
|
||||
context.title = _("Jobs")
|
||||
|
||||
@@ -284,7 +284,7 @@ class ProcessPayroll(Document):
|
||||
})
|
||||
|
||||
# Deductions
|
||||
for acc, amt in deductions.items():
|
||||
for acc, amount in deductions.items():
|
||||
payable_amount -= flt(amount, precision)
|
||||
accounts.append({
|
||||
"account": acc,
|
||||
|
||||
@@ -39,6 +39,7 @@ 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();
|
||||
|
||||
@@ -36,7 +36,7 @@ def execute(filters=None):
|
||||
status_map = {"Present": "P", "Absent": "A", "Half Day": "HD", "On Leave": "L", "None": "", "Holiday":"<b>H</b>"}
|
||||
if status == "None" and holiday_map:
|
||||
emp_holiday_list = emp_det.holiday_list if emp_det.holiday_list else default_holiday_list
|
||||
if (day+1) in holiday_map[emp_holiday_list]:
|
||||
if emp_holiday_list in holiday_map and (day+1) in holiday_map[emp_holiday_list]:
|
||||
status = "Holiday"
|
||||
row.append(status_map[status])
|
||||
|
||||
@@ -45,7 +45,7 @@ def execute(filters=None):
|
||||
elif status == "Absent":
|
||||
total_a += 1
|
||||
elif status == "On Leave":
|
||||
total_l += 1
|
||||
total_l += 1
|
||||
elif status == "Half Day":
|
||||
total_p += 0.5
|
||||
total_a += 0.5
|
||||
|
||||
@@ -322,6 +322,36 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "currency_detail",
|
||||
"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": "",
|
||||
"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,
|
||||
@@ -359,8 +389,8 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "currency_detail",
|
||||
"fieldtype": "Section Break",
|
||||
"fieldname": "column_break_12",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@@ -368,7 +398,6 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@@ -414,35 +443,6 @@
|
||||
"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": 0,
|
||||
@@ -1322,7 +1322,7 @@
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:!doc.__islocal",
|
||||
"fieldname": "section_break0",
|
||||
@@ -1671,7 +1671,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-08-23 14:09:30.492628",
|
||||
"modified": "2017-10-23 14:56:21.991160",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM",
|
||||
|
||||
@@ -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,
|
||||
'rate' : rate / self.conversion_rate if self.conversion_rate else 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,22 +147,28 @@ class BOM(WebsiteGenerator):
|
||||
if arg.get('scrap_items'):
|
||||
rate = self.get_valuation_rate(arg)
|
||||
elif arg:
|
||||
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):
|
||||
if arg.get('bom_no') and 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)
|
||||
|
||||
@@ -312,7 +318,7 @@ class BOM(WebsiteGenerator):
|
||||
li.append("{0} on row {1}".format(i.item_code, i.idx))
|
||||
duplicate_list = '<br>' + '<br>'.join(li)
|
||||
|
||||
frappe.throw(_("Same item has been entered multiple times. {list}").format(list=duplicate_list))
|
||||
frappe.throw(_("Same item has been entered multiple times. {0}").format(duplicate_list))
|
||||
|
||||
def check_recursion(self):
|
||||
""" Check whether recursion occurs in any bom"""
|
||||
@@ -346,7 +352,7 @@ class BOM(WebsiteGenerator):
|
||||
count = 0
|
||||
if not bom_list:
|
||||
bom_list = []
|
||||
|
||||
|
||||
if self.name not in bom_list:
|
||||
bom_list.append(self.name)
|
||||
|
||||
@@ -374,7 +380,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)
|
||||
d.hour_rate = hour_rate / flt(self.conversion_rate) if self.conversion_rate else hour_rate
|
||||
|
||||
if d.hour_rate and d.time_in_mins:
|
||||
d.base_hour_rate = flt(d.hour_rate) * flt(self.conversion_rate)
|
||||
|
||||
@@ -30,12 +30,13 @@ class BOMUpdateTool(Document):
|
||||
frappe.throw(_("The selected BOMs are not for the same item"))
|
||||
|
||||
def update_new_bom(self):
|
||||
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
|
||||
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
|
||||
|
||||
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, current_bom_unitcost, current_bom_unitcost, self.current_bom))
|
||||
(self.new_bom, new_bom_unitcost, new_bom_unitcost, self.current_bom))
|
||||
|
||||
def get_parent_boms(self):
|
||||
return [d[0] for d in frappe.db.sql("""select distinct parent
|
||||
|
||||
@@ -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,8 +370,13 @@ class ProductionOrder(Document):
|
||||
self.actual_start_date = None
|
||||
self.actual_end_date = None
|
||||
if self.get("operations"):
|
||||
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")])
|
||||
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)
|
||||
|
||||
def delete_timesheet(self):
|
||||
for timesheet in frappe.get_all("Timesheet", ["name"], {"production_order": self.name}):
|
||||
@@ -411,18 +416,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)
|
||||
|
||||
@@ -439,7 +444,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):
|
||||
@@ -463,12 +468,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 {}
|
||||
|
||||
@@ -611,14 +616,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
|
||||
|
||||
@@ -59,8 +59,6 @@ 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"),
|
||||
|
||||
@@ -12,10 +12,6 @@ from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
|
||||
from erpnext.manufacturing.doctype.production_order.production_order import get_item_details
|
||||
|
||||
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, [])
|
||||
|
||||
@@ -398,6 +394,9 @@ 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]])
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<h1 class="text-left"><b>{%= __("BOM Stock Report") %}</b></h1>
|
||||
<h5 class="text-left">{%= filters.bom %}</h5>
|
||||
<h5 class="text-left">{%= filters.warehouse %}</h5>
|
||||
<hr>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 15%">{%= __("Item") %}</th>
|
||||
<th style="width: 35%">{%= __("Description") %}</th>
|
||||
<th style="width: 14%">{%= __("Required Qty") %}</th>
|
||||
<th style="width: 13%">{%= __("In Stock Qty") %}</th>
|
||||
<th style="width: 23%">{%= __("Enough Parts to Build") %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for(var i=0, l=data.length; i<l; i++) { %}
|
||||
<tr>
|
||||
<td>{%= data[i][ __("Item")] %}</td>
|
||||
<td>{%= data[i][ __("Description")] %} </td>
|
||||
<td align="right">{%= data[i][ __("Required Qty")] %} </td>
|
||||
<td align="right">{%= data[i][ __("In Stock Qty")] %} </td>
|
||||
<td align="right">{%= data[i][ __("Enough Parts to Build")] %} </td>
|
||||
</tr>
|
||||
{% } %}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -408,6 +408,8 @@ 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
|
||||
@@ -432,16 +434,22 @@ 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
|
||||
erpnext.patches.v8_7.add_more_gst_fields #21-09-2017
|
||||
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
|
||||
erpnext.patches.v8_9.add_setup_progress_actions #08-09-2017 #26-09-2017
|
||||
erpnext.patches.v8_9.rename_company_sales_target_field
|
||||
erpnext.patches.v8_8.set_bom_rate_as_per_uom
|
||||
erpnext.patches.v9_0.remove_subscription_module
|
||||
erpnext.patches.v8_7.make_subscription_from_recurring_data
|
||||
erpnext.patches.v8_8.add_new_fields_in_accounts_settings
|
||||
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 #2017-10-23
|
||||
erpnext.patches.v9_0.add_healthcare_domain
|
||||
erpnext.patches.v9_0.set_variant_item_description
|
||||
erpnext.patches.v9_0.set_uoms_in_variant_field
|
||||
erpnext.patches.v9_0.copy_old_fees_field_data
|
||||
|
||||
@@ -12,6 +12,7 @@ def execute():
|
||||
|
||||
domains = frappe.db.sql_list("select domain from tabCompany")
|
||||
if "Education" not in domains:
|
||||
role = frappe.get_doc("Role", "Instructor")
|
||||
role.disabled = 1
|
||||
role.save(ignore_permissions=True)
|
||||
if frappe.db.exists("Role", "Instructor"):
|
||||
role = frappe.get_doc("Role", "Instructor")
|
||||
role.disabled = 1
|
||||
role.save(ignore_permissions=True)
|
||||
@@ -8,28 +8,43 @@ 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']:
|
||||
for data in get_data(doctype):
|
||||
make_subscription(doctype, data)
|
||||
for doctype in ['Sales Order', 'Sales Invoice', 'Purchase Order', 'Purchase Invoice']:
|
||||
date_field = "transaction_date"
|
||||
if doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||
date_field = "posting_date"
|
||||
|
||||
def get_data(doctype):
|
||||
return frappe.db.sql(""" select name, from_date, end_date, recurring_type,recurring_id,
|
||||
for data in get_data(doctype, date_field):
|
||||
make_subscription(doctype, data, date_field)
|
||||
|
||||
def get_data(doctype, date_field):
|
||||
return frappe.db.sql(""" select name, from_date, end_date, recurring_type, recurring_id,
|
||||
next_date, notify_by_email, notification_email_address, recurring_print_format,
|
||||
repeat_on_day_of_month, submit_on_creation, docstatus
|
||||
from `tab{0}` where is_recurring = 1 and next_date >= %s and docstatus < 2
|
||||
""".format(doctype), today(), as_dict=1)
|
||||
repeat_on_day_of_month, submit_on_creation, docstatus, {0}
|
||||
from `tab{1}` where is_recurring = 1 and next_date >= %s and docstatus < 2
|
||||
order by next_date desc
|
||||
""".format(date_field, doctype), today(), as_dict=1)
|
||||
|
||||
def make_subscription(doctype, data, date_field):
|
||||
if data.name == data.recurring_id:
|
||||
start_date = data.get(date_field)
|
||||
else:
|
||||
start_date = frappe.db.get_value(doctype, data.recurring_id, date_field)
|
||||
|
||||
def make_subscription(doctype, data):
|
||||
doc = frappe.get_doc({
|
||||
'doctype': 'Subscription',
|
||||
'reference_doctype': doctype,
|
||||
'reference_document': data.name,
|
||||
'start_date': data.from_date,
|
||||
'reference_document': data.recurring_id,
|
||||
'start_date': start_date,
|
||||
'end_date': data.end_date,
|
||||
'frequency': data.recurring_type,
|
||||
'repeat_on_day': data.repeat_on_day_of_month,
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
frappe.db.sql(
|
||||
"INSERT INTO `tabSingles` (`doctype`, `field`, `value`) VALUES ('Accounts Settings', 'allow_stale', '1'), "
|
||||
"('Accounts Settings', 'stale_days', '1')"
|
||||
)
|
||||
@@ -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": '[]' }
|
||||
]
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Copyright (c) 2017, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc('stock', 'doctype', 'item_variant_settings')
|
||||
frappe.reload_doc('stock', 'doctype', 'variant_field')
|
||||
|
||||
doc = frappe.get_doc('Item Variant Settings')
|
||||
doc.set_default_fields()
|
||||
doc.save()
|
||||
@@ -0,0 +1,15 @@
|
||||
# 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))
|
||||
14
erpnext/patches/v9_0/add_healthcare_domain.py
Normal file
14
erpnext/patches/v9_0/add_healthcare_domain.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# 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)
|
||||
11
erpnext/patches/v9_0/copy_old_fees_field_data.py
Normal file
11
erpnext/patches/v9_0/copy_old_fees_field_data.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# Copyright (c) 2017, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
if "total_amount" not in frappe.db.get_table_columns("Fees"):
|
||||
return
|
||||
|
||||
frappe.db.sql("""update tabFees set grand_total=total_amount where grand_total = 0.0""")
|
||||
43
erpnext/patches/v9_0/fix_subscription_next_date.py
Normal file
43
erpnext/patches/v9_0/fix_subscription_next_date.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# Copyright (c) 2017, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import getdate
|
||||
from erpnext.accounts.doctype.subscription.subscription import get_next_schedule_date
|
||||
|
||||
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",
|
||||
"start_date", "frequency", "repeat_on_day"],
|
||||
filters = {'reference_doctype': ('in', doctypes), 'docstatus': 1}):
|
||||
|
||||
recurring_id = frappe.db.get_value(data.reference_doctype, data.reference_document, "recurring_id")
|
||||
if recurring_id:
|
||||
frappe.db.sql("update `tab{0}` set subscription=%s where recurring_id=%s"
|
||||
.format(data.reference_doctype), (data.name, recurring_id))
|
||||
|
||||
date_field = 'transaction_date'
|
||||
if data.reference_doctype in ['Sales Invoice', 'Purchase Invoice']:
|
||||
date_field = 'posting_date'
|
||||
|
||||
start_date = frappe.db.get_value(data.reference_doctype, data.reference_document, date_field)
|
||||
|
||||
if start_date and getdate(start_date) != getdate(data.start_date):
|
||||
last_ref_date = frappe.db.sql("""
|
||||
select {0}
|
||||
from `tab{1}`
|
||||
where subscription=%s and docstatus < 2
|
||||
order by creation desc
|
||||
limit 1
|
||||
""".format(date_field, data.reference_doctype), data.name)[0][0]
|
||||
|
||||
next_schedule_date = get_next_schedule_date(last_ref_date, data.frequency, data.repeat_on_day)
|
||||
|
||||
frappe.db.set_value("Subscription", data.name, {
|
||||
"start_date": start_date,
|
||||
"next_schedule_date": next_schedule_date
|
||||
}, None)
|
||||
14
erpnext/patches/v9_0/set_uoms_in_variant_field.py
Normal file
14
erpnext/patches/v9_0/set_uoms_in_variant_field.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
doc = frappe.get_doc('Item Variant Settings')
|
||||
variant_field_names = [vf.field_name for vf in doc.fields]
|
||||
if 'uoms' not in variant_field_names:
|
||||
doc.append(
|
||||
'fields', {
|
||||
'field_name': 'uoms'
|
||||
}
|
||||
)
|
||||
doc.save()
|
||||
45
erpnext/patches/v9_0/set_variant_item_description.py
Normal file
45
erpnext/patches/v9_0/set_variant_item_description.py
Normal file
@@ -0,0 +1,45 @@
|
||||
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)
|
||||
@@ -53,12 +53,17 @@ 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):
|
||||
|
||||
@@ -47,7 +47,7 @@ class Task(Document):
|
||||
|
||||
from frappe.desk.form.assign_to import clear
|
||||
clear(self.doctype, self.name)
|
||||
|
||||
|
||||
def validate_progress(self):
|
||||
if self.progress > 100:
|
||||
frappe.throw(_("Progress % for a task cannot be more than 100."))
|
||||
@@ -63,6 +63,12 @@ class Task(Document):
|
||||
self.check_recursion()
|
||||
self.reschedule_dependent_tasks()
|
||||
self.update_project()
|
||||
self.unassign_todo()
|
||||
|
||||
def unassign_todo(self):
|
||||
if self.status == "Closed" or self.status == "Cancelled":
|
||||
from frappe.desk.form.assign_to import clear
|
||||
clear(self.doctype, self.name)
|
||||
|
||||
def update_total_expense_claim(self):
|
||||
self.total_expense_claim = frappe.db.sql("""select sum(total_sanctioned_amount) from `tabExpense Claim`
|
||||
@@ -120,7 +126,7 @@ class Task(Document):
|
||||
def has_webform_permission(doc):
|
||||
project_user = frappe.db.get_value("Project User", {"parent": doc.project, "user":frappe.session.user} , "user")
|
||||
if project_user:
|
||||
return True
|
||||
return True
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_events(start, end, filters=None):
|
||||
@@ -154,7 +160,7 @@ def get_project(doctype, txt, searchfield, start, page_len, filters):
|
||||
order by name
|
||||
limit %(start)s, %(page_len)s """ % {'key': searchfield,
|
||||
'txt': "%%%s%%" % frappe.db.escape(txt), 'mcond':get_match_cond(doctype),
|
||||
'start': start, 'page_len': page_len})
|
||||
'start': start, 'page_len': page_len})
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@@ -170,4 +176,5 @@ def set_tasks_as_overdue():
|
||||
where exp_end_date is not null
|
||||
and exp_end_date < CURDATE()
|
||||
and `status` not in ('Closed', 'Cancelled')""")
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -159,9 +159,14 @@ frappe.ui.form.on("Timesheet Detail", {
|
||||
});
|
||||
|
||||
var calculate_end_time = function(frm, cdt, cdn) {
|
||||
var child = locals[cdt][cdn];
|
||||
let child = locals[cdt][cdn];
|
||||
|
||||
var d = moment(child.from_time);
|
||||
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);
|
||||
if(child.hours) {
|
||||
d.add(child.hours, "hours");
|
||||
frm._setting_hours = true;
|
||||
|
||||
@@ -1,39 +1,45 @@
|
||||
frappe.ui.form.on("Communication", {
|
||||
refresh: function(frm) {
|
||||
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";
|
||||
if(frm.doc.reference_doctype !== "Issue") {
|
||||
frm.add_custom_button(__("Issue"), function() {
|
||||
frappe.confirm("Are you sure you want to create Issue from this email", function(){
|
||||
frm.add_custom_button(__("Issue"), () => {
|
||||
frappe.confirm(__(confirm_msg, [__("Issue")]), () => {
|
||||
frm.trigger('make_issue_from_communication');
|
||||
})
|
||||
}, "Make");
|
||||
}
|
||||
|
||||
if(!in_list(["Lead", "Opportunity"], frm.doc.reference_doctype)) {
|
||||
frm.add_custom_button(__("Lead"), function() {
|
||||
frappe.confirm("Are you sure you want to create Lead from this email", function(){
|
||||
frm.add_custom_button(__("Lead"), () => {
|
||||
frappe.confirm(__(confirm_msg, [__("Lead")]), () => {
|
||||
frm.trigger('make_lead_from_communication');
|
||||
})
|
||||
}, "Make");
|
||||
|
||||
frm.add_custom_button(__("Opportunity"), function() {
|
||||
frappe.confirm("Are you sure you want to create Opportunity from this email", function(){
|
||||
frm.add_custom_button(__("Opportunity"), () => {
|
||||
frappe.confirm(__(confirm_msg, [__("Opportunity")]), () => {
|
||||
frm.trigger('make_opportunity_from_communication');
|
||||
})
|
||||
}, "Make");
|
||||
}
|
||||
|
||||
|
||||
frm.page.set_inner_btn_group_as_primary(__("Make"));
|
||||
},
|
||||
|
||||
make_lead_from_communication: function(frm) {
|
||||
make_lead_from_communication: (frm) => {
|
||||
return frappe.call({
|
||||
method: "frappe.email.inbox.make_lead_from_communication",
|
||||
args: {
|
||||
communication: frm.doc.name
|
||||
},
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
callback: (r) => {
|
||||
if(r.message) {
|
||||
frm.reload_doc()
|
||||
}
|
||||
@@ -41,14 +47,14 @@ frappe.ui.form.on("Communication", {
|
||||
})
|
||||
},
|
||||
|
||||
make_issue_from_communication: function(frm) {
|
||||
make_issue_from_communication: (frm) => {
|
||||
return frappe.call({
|
||||
method: "frappe.email.inbox.make_issue_from_communication",
|
||||
args: {
|
||||
communication: frm.doc.name
|
||||
},
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
callback: (r) => {
|
||||
if(r.message) {
|
||||
frm.reload_doc()
|
||||
}
|
||||
@@ -56,14 +62,14 @@ frappe.ui.form.on("Communication", {
|
||||
})
|
||||
},
|
||||
|
||||
make_opportunity_from_communication: function(frm) {
|
||||
make_opportunity_from_communication: (frm) => {
|
||||
return frappe.call({
|
||||
method: "frappe.email.inbox.make_opportunity_from_communication",
|
||||
args: {
|
||||
communication: frm.doc.name
|
||||
},
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
callback: (r) => {
|
||||
if(r.message) {
|
||||
frm.reload_doc()
|
||||
}
|
||||
|
||||
@@ -101,27 +101,6 @@ 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;
|
||||
@@ -153,11 +132,36 @@ 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() {
|
||||
@@ -195,13 +199,12 @@ 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)) {
|
||||
this.apply_default_taxes();
|
||||
frappe.after_ajax(() => this.apply_default_taxes());
|
||||
} else if(this.frm.doc.__islocal && this.frm.doc.company && this.frm.doc["items"]
|
||||
&& !this.frm.doc.is_pos) {
|
||||
me.calculate_taxes_and_totals();
|
||||
frappe.after_ajax(() => this.calculate_taxes_and_totals());
|
||||
}
|
||||
if(frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "item_code")) {
|
||||
this.setup_item_selector();
|
||||
@@ -378,6 +381,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
if(me.frm.doc.company && me.frm.fields_dict.currency) {
|
||||
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);
|
||||
}
|
||||
@@ -519,6 +523,7 @@ 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);
|
||||
}
|
||||
@@ -536,6 +541,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
}
|
||||
|
||||
}
|
||||
// Make read only if Accounts Settings doesn't allow stale rates
|
||||
this.frm.set_df_property("conversion_rate", "read_only", erpnext.stale_rate_allowed());
|
||||
},
|
||||
|
||||
set_actual_charges_based_on_currency: function() {
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
<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>
|
||||
@@ -52,7 +51,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% } %}
|
||||
<div class="pos-list-row grand-total-area collapse-btn" style="border-bottom:1px solid #d1d8dd;">
|
||||
<div class="cell">
|
||||
<a class="">
|
||||
|
||||
@@ -86,6 +86,10 @@ 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;
|
||||
}
|
||||
@@ -135,10 +139,6 @@ 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;
|
||||
},
|
||||
|
||||
|
||||
@@ -37,6 +37,10 @@ $.extend(erpnext, {
|
||||
}
|
||||
},
|
||||
|
||||
stale_rate_allowed: () => {
|
||||
return cint(frappe.boot.sysdefaults.allow_stale) || 1;
|
||||
},
|
||||
|
||||
setup_serial_no: function() {
|
||||
var grid_row = cur_frm.open_grid_row();
|
||||
if(!grid_row || !grid_row.grid_form.fields_dict.serial_no ||
|
||||
|
||||
@@ -96,7 +96,17 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
||||
|
||||
if(this.show_dialog) {
|
||||
let d = this.item;
|
||||
this.dialog.set_value('serial_no', d.serial_no);
|
||||
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.show();
|
||||
@@ -116,8 +126,10 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
||||
}
|
||||
values.batches.map((batch, i) => {
|
||||
if(!batch.selected_qty || batch.selected_qty === 0 ) {
|
||||
frappe.throw(__("Please select quantity on row " + (i+1)));
|
||||
return false;
|
||||
if (!this.show_dialog) {
|
||||
frappe.throw(__("Please select quantity on row " + (i+1)));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
@@ -125,9 +137,11 @@ erpnext.SerialNoBatchSelector = Class.extend({
|
||||
} else {
|
||||
let serial_nos = values.serial_no || '';
|
||||
if (!serial_nos || !serial_nos.replace(/\s/g, '').length) {
|
||||
frappe.throw(__("Please enter serial numbers for serialized item "
|
||||
+ values.item_code));
|
||||
return false;
|
||||
if (!this.show_dialog) {
|
||||
frappe.throw(__("Please enter serial numbers for serialized item "
|
||||
+ values.item_code));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -84,34 +84,13 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-08-31 14:38:52.220743",
|
||||
"modified_by": "ewdszx@ed.ews",
|
||||
"modified": "2017-09-29 14:38:52.220743",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Regional",
|
||||
"name": "GST HSN Code",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user