Compare commits

..

82 Commits

Author SHA1 Message Date
Nabin Hait
d98a6d81eb Merge branch 'hotfix' 2018-03-13 16:14:02 +05:30
Nabin Hait
baac05955b bumped to version 10.1.9 2018-03-13 16:44:02 +06:00
Nabin Hait
89346967da Reverted #13259 (#13291) 2018-03-13 16:01:11 +05:30
Nabin Hait
97e890d0b3 Fix in unallocated amount calc in payment entry (#13288) 2018-03-13 15:57:34 +05:30
Ameya Shenoy
95763bc233 Purchase Order fixes (#13287)
- Reason: If Item Code was changed in Item Master, it didn't used to reflect on the 'Purchase Order Item Supplied' and 'Purchase Reciept Item Supplied'
- Data field in 'Production Order Item Supplied' and 'Production Reciept Item Supplied' converted to Link Field.
2018-03-13 15:51:56 +05:30
Nabin Hait
96f4b22589 Set po nos in DN and SI (#13290) 2018-03-13 15:46:59 +05:30
rohitwaghchaure
13460f023d [Fix] Batch value showing as item value while making batch (#13281) 2018-03-12 17:20:50 +05:30
Nabin Hait
3907eae83f Merge branch 'hotfix' 2018-03-12 15:31:03 +05:30
Nabin Hait
04f50be306 bumped to version 10.1.8 2018-03-12 16:01:03 +06:00
Ameya Shenoy
b34ab7549a fixed subject to task_name (#13278) 2018-03-12 15:24:01 +05:30
Shreya Shah
aa54d934b8 make stock qty negative (#13276) 2018-03-12 15:23:28 +05:30
Nabin Hait
2c7a6e6b43 Reserve for subcontracting (#13195)
* [fix] #8427

* review comments changes

* Validation for reserved warhouse

* code improvements

* alignment

* test case

* message changes

* default warehouse / remove validation / change sql

* fix

* patch

* Fixed merge conflict

* Fixes and cleanups of reserve qty for subcontracting

* set from_warehouse only if purchase_order and purpose found (#12398)

* [HotFix] Validation issue for subcontract stock entry (#12127)

* [Fix] Validation issue for subcontract stock entry

* Update stock_entry.py

* Fixes and cleanups of reserve qty for subcontracting

* patch fixed

* Reload bin in patch

* [fix] set source warehouse in stock entry for manufacture

* [fix] #8540

* code alignment

* code alignment

* Move target warehouse validation to submit

* validation code improvement

* code changes for single stock entry

* validation fix

* call make_rm_stock_entry

* remove old stock entry method/rewrite test case

* Don't set bom_no against raw materials while trasferring items for sub-contracting

* minor fix
2018-03-12 14:12:12 +05:30
Shreya Shah
58797481f0 uncheck report hide (#13256) 2018-03-12 13:08:01 +05:30
rohitwaghchaure
12aa4265ff Don't allow to set negative quantity if transaction is not return entry (#13255) 2018-03-12 11:20:30 +05:30
Shreya Shah
69d9f51dbb if price list not found, set default selling/ buying price list from settings (#13259) 2018-03-12 11:15:49 +05:30
Nabin Hait
3dea6589d7 Merge branch 'hotfix' 2018-03-09 16:38:51 +05:30
Nabin Hait
096d136460 bumped to version 10.1.7 2018-03-09 17:08:50 +06:00
Nabin Hait
c3889d85a1 Update shopping_cart.js 2018-03-09 16:28:25 +05:30
Nabin Hait
052333ea63 subctracting stock entry should also be considered as consumed qty 2018-03-09 14:30:32 +05:30
Shreya Shah
aadd30d194 fetch delivery date on add-row only if item_code exists (#13250) 2018-03-09 12:19:35 +05:30
Harsh Patel
22e6f8d4c2 Fixes for #13071 (#13234) 2018-03-08 16:23:59 +05:30
rohitwaghchaure
acd3479269 Bank POS transactions in bank reconciliation (#13225) 2018-03-08 16:22:39 +05:30
rohitwaghchaure
a32e57ae82 [Fix] Discount in offline POS is enabled even is it's disabled in POS Profile (#13233) 2018-03-08 16:21:13 +05:30
Ranjith Kurungadam
a3a63177a1 fix- cannot cancel consultation, remove updating age (#13226) 2018-03-08 16:20:22 +05:30
tundebabzy
1643cce479 fetch journal entry details (#13218) 2018-03-08 11:17:17 +05:30
Manas Solanki
bb42e3b411 Merge pull request #13214 from manassolanki/assessment-report-fix
minor fix for key error in assessment reports
2018-03-07 16:30:35 +05:30
Manas Solanki
586e9400b1 minor fix for key error 2018-03-07 16:28:21 +05:30
Vishal Dhayagude
668ec25d19 Allow Item variant Rename (#13161)
* [WIP] Item varient rename

* [wip] Item Variant

* [fix] Item Varient Rename

* [fix] Item Attribution Rename

* removed unwanted code
2018-03-07 15:31:08 +05:30
Nabin Hait
f0543a9765 Merge branch 'hotfix' 2018-03-07 13:09:06 +05:30
Nabin Hait
36b4faab48 bumped to version 10.1.6 2018-03-07 13:39:05 +06:00
Saurabh
2b59a851c4 Merge pull request #13166 from saurabh6790/patches_fixes
[fix] use join instead of subquery
2018-03-07 12:59:03 +05:30
Manas Solanki
857d4f7a0b Merge pull request #13209 from manassolanki/guardian-fix
allow renaming of the guardian
2018-03-07 12:53:40 +05:30
Manas Solanki
12d7bfb658 allow renaming of the guardian 2018-03-07 12:52:31 +05:30
Saurabh
ca6e223694 [fix] use join instead of subquery 2018-03-07 12:13:32 +05:30
rohitwaghchaure
343ba85e3a Merge pull request #13203 from saurabh6790/pe_fix
[fix] setup_party_account_field on init
2018-03-07 12:11:46 +05:30
Manas Solanki
16c324f699 Merge pull request #13207 from manassolanki/course-schedule-fix
fix the desktop icon for course schedule
2018-03-07 12:10:36 +05:30
Manas Solanki
1fa992564a fix the desktop icon for course schedule 2018-03-07 12:08:52 +05:30
Saurabh
a65b28772f [fix] setup_party_account_field on init 2018-03-06 18:22:01 +05:30
Nabin Hait
ba6003ece7 Merge branch 'hotfix' 2018-03-05 14:47:19 +05:30
Nabin Hait
491ce05b8d bumped to version 10.1.5 2018-03-05 15:17:18 +06:00
Saurabh
2cfcbf933d Patches fixes (#13163)
* [fix] if serialised items not found then return

* [fix] unicode encoding in patch
2018-03-05 13:01:15 +05:30
Zarrar
2550180a05 remove updating territory for customers on change (#13162) 2018-03-05 12:32:41 +05:30
Manas Solanki
5fd7b3bb80 Fix status updater (#13033)
* fix the status updater for multiple sources

* patch for updating the status

* patch for updating the sales order item

* Update update_status_for_multiple_source_in_po.py
2018-03-05 11:28:29 +05:30
Vishal Dhayagude
35b665cb26 [new] Create multiple warehouse address and fetch address to stock entry (#13109)
* [new] Create multiple warehouse address and fetch address to stock entry

* [fix] Deleted unwanted field and added patch to link warehouse details to Address

* [fix] Codacy fixed

* [fix] Modified patch for warehouse address

* [fix] Modified patch for warehouse address

* [fix] Patch updated and removed contact details from stock entry

* [fix] Patch Updated
2018-03-05 11:10:01 +05:30
rohitwaghchaure
a6a4e86dc8 [Fix] System only shows 20 attribute values while making variants (#13155) 2018-03-05 11:03:59 +05:30
rohitwaghchaure
0eb8bb2511 [Fix] Wrong salary slips showing when click on view salary slips from payroll entry (#13152) 2018-03-05 11:03:27 +05:30
rohitwaghchaure
67cfa81de2 [Fix] Stock Ageing report does not work with group warehouse (#13151) 2018-03-05 11:00:42 +05:30
rohitwaghchaure
a942722619 [Fix] Timeout issue while saving multilevel BOM (#13118) 2018-03-01 13:15:25 +05:30
Nabin Hait
c10bbd6aa7 Editable unallocated amount in pe (#13130)
* editable unallocated amount in payment entry to handle multi currency

* set unallocated amount on server side

* some minor fixes

* Fixes in territory patch

* removed print

* minor fixes
2018-03-01 13:14:14 +05:30
rohitwaghchaure
4badb45ee5 Merge pull request #13139 from rohitwaghchaure/sales_invoice_email_propmt_issue
[Fix] Sales invoice email prompt not working
2018-03-01 13:07:31 +05:30
Rohit Waghchaure
855d843e55 [Fix] Sales invoice email prompt not working 2018-03-01 13:06:01 +05:30
rohitwaghchaure
0df95fa781 Multi-UOM for sales/purchase return (#13132)
* Multi-UOM for sales/purchase return

* Update sales_and_purchase_return.py
2018-03-01 11:31:33 +05:30
Zarrar
6578bc11b6 wrong query formed to delete events (#13119) 2018-03-01 10:54:55 +05:30
Nabin Hait
f68dc69078 Set auto created serial nos in incoming transactions in case of multi UOM (#13112)
* Create user from Employee

* Set auto created serial nos in incoming transactions in case of multi uom
2018-03-01 10:54:24 +05:30
Zarrar
502af4dd67 display image fnd description for root BOM also (#13099) 2018-03-01 10:44:47 +05:30
rohitwaghchaure
db9fa78ee8 Do not validate payment schedule for POS (#13115) 2018-03-01 10:32:29 +05:30
Nabin Hait
a645f36b2b Get valuation rate from historical SLE even if it is zero (#13129)
* Don't overwrite start and end date comes from payroll entry

* Get valuation rate from historical SLE even if it is zero, if records exists

* Valid till should be autoset if not any default value

* Set status of expense claim based on is_paid check
2018-03-01 10:31:24 +05:30
rohitwaghchaure
332a17ee86 [Fix] Test case for serial no (#13136) 2018-03-01 10:28:04 +05:30
Shreya Shah
a310cc7156 order by modified instead of item name (#13113) 2018-02-28 18:59:55 +05:30
Nabin Hait
3b43c0d160 Update territory and customer_group patch optimization (#13076)
* Update territory and customer_group patch optimization

* Update update_territory_and_customer_group.py
2018-02-27 15:57:14 +05:30
Zarrar
96002c28bd optimize patch for faster execution (#13068) 2018-02-27 15:57:01 +05:30
Zarrar
91fc1a8fbe Update Territory & Customer Group across all transaction (#13004)
* added method for update query based on changes

* patch added

* updated function, moved util function
2018-02-27 15:56:38 +05:30
rohitwaghchaure
f1755fb5b1 Merge pull request #13094 from rohitwaghchaure/pos_discount_v10_1
[Hotfix] POS discount issue
2018-02-27 11:58:33 +05:30
Manas Solanki
be841ccc53 Merge pull request #13096 from manassolanki/fix-fee-schedule
Fixes in the Fees Schedule
2018-02-27 11:23:38 +05:30
Manas Solanki
51dfba749b filter students on basis of category and button to show fees 2018-02-26 19:32:26 +05:30
Nabin Hait
3799f8bec9 Don't validate serial nos while cancelling the transaction 2018-02-26 16:39:06 +05:30
Rohit Waghchaure
3edc101957 [Fix] POS discount issue 2018-02-26 13:25:58 +05:30
Nabin Hait
127c61e930 Fixed logic in itemwise recommended reorder level 2018-02-26 12:43:17 +05:30
Nabin Hait
e55831a89c Projected Qty in Auto reorder email 2018-02-26 11:38:40 +05:30
Nabin Hait
4ff4d185f7 Merge branch 'hotfix' 2018-02-23 16:59:42 +05:30
Nabin Hait
d54953e419 bumped to version 10.1.4 2018-02-23 17:29:42 +06:00
Zarrar
339426c926 fix permission issue for stock balance report - Item Group (#13069) 2018-02-23 16:58:55 +05:30
Nabin Hait
489ff6e21c Merge branch 'hotfix' 2018-02-23 16:49:41 +05:30
Nabin Hait
d12fb58c12 bumped to version 10.1.3 2018-02-23 17:19:40 +06:00
Ameya Shenoy
82048cf3ce verify payment entry amount is positive (#13066)
* verify payment entry amount is positive

* Update sales_invoice.py

* Update sales_invoice.py
2018-02-23 16:33:28 +05:30
rohitwaghchaure
4d76269eeb [Fix] Item wise sales register report (#13055) 2018-02-23 16:25:30 +05:30
Shreya Shah
d5b2e39f45 improve validation (#13058) 2018-02-23 16:23:57 +05:30
rohitwaghchaure
dd0fc1084e [Fix] PDC amount, PDC print layout issue (#13062) 2018-02-23 16:20:46 +05:30
Nabin Hait
d283ee73f3 Fetch timesheet based on project 2018-02-23 12:58:39 +05:30
Nabin Hait
93f138eece Merge branch 'hotfix' 2018-02-22 18:18:34 +05:30
Nabin Hait
2b2cf13408 bumped to version 10.1.2 2018-02-22 18:48:34 +06:00
Nabin Hait
7f49b57aaa Update added_extra_gst_custom_field_in_gstr2.py 2018-02-22 18:17:57 +05:30
107 changed files with 1933 additions and 411 deletions

View File

@@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
__version__ = '10.1.1'
__version__ = '10.1.9'
def get_default_company(user=None):
'''Get default company for user'''

View File

@@ -169,10 +169,10 @@ class Account(NestedSet):
# Add company abbr if not provided
from erpnext.setup.doctype.company.company import get_name_with_abbr
new_account = get_name_with_abbr(new, self.company)
new_account = get_name_with_number(new_account, self.account_number)
# Validate properties before merging
if merge:
if not merge:
new_account = get_name_with_number(new_account, self.account_number)
else:
# Validate properties before merging
if not frappe.db.exists("Account", new):
throw(_("Account {0} does not exist").format(new))

View File

@@ -159,6 +159,36 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "include_pos_transactions",
"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": "Include POS Transactions",
"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,
@@ -292,7 +322,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-04-21 16:58:26.902732",
"modified": "2018-03-07 18:58:48.658687",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Reconciliation",

View File

@@ -53,10 +53,26 @@ class BankReconciliation(Document):
posting_date ASC, name DESC
""".format(condition),
{"account":self.bank_account, "from":self.from_date, "to":self.to_date}, as_dict=1)
entries = sorted(list(payment_entries)+list(journal_entries),
pos_entries = []
if self.include_pos_transactions:
pos_entries = frappe.db.sql("""
select
"Sales Invoice Payment" as payment_document, sip.name as payment_entry, sip.amount as debit,
si.posting_date, si.debit_to as against_account, sip.clearance_date,
account.account_currency, 0 as credit
from `tabSales Invoice Payment` sip, `tabSales Invoice` si, `tabAccount` account
where
sip.account=%(account)s and si.docstatus=1 and sip.parent = si.name
and account.name = sip.account and si.posting_date >= %(from)s and si.posting_date <= %(to)s {0}
order by
si.posting_date ASC, si.name DESC
""".format(condition),
{"account":self.bank_account, "from":self.from_date, "to":self.to_date}, as_dict=1)
entries = sorted(list(payment_entries)+list(journal_entries+list(pos_entries)),
key=lambda k: k['posting_date'] or getdate(nowdate()))
self.set('payment_entries', [])
self.total_amount = 0.0

View File

@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import unittest
class TestBankReconciliation(unittest.TestCase):
pass

View File

@@ -145,13 +145,13 @@ frappe.ui.form.on('Payment Entry', {
frm.doc.paid_amount : frm.doc.received_amount;
frm.toggle_display("write_off_difference_amount", (frm.doc.difference_amount && frm.doc.party &&
(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) &&
(frm.doc.total_allocated_amount > party_amount)));
frm.toggle_display("set_exchange_gain_loss",
(frm.doc.paid_amount && frm.doc.received_amount && frm.doc.difference_amount &&
(frm.doc.paid_from_account_currency != company_currency ||
frm.doc.paid_to_account_currency != company_currency)));
((frm.doc.paid_from_account_currency != company_currency ||
frm.doc.paid_to_account_currency != company_currency) &&
frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency)));
frm.refresh_fields();
},
@@ -300,7 +300,15 @@ frappe.ui.form.on('Payment Entry', {
if(frm.doc.payment_type == "Pay") {
frm.events.get_outstanding_documents(frm);
} else if (frm.doc.payment_type == "Receive") {
frm.events.received_amount(frm);
if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
if(frm.doc.source_exchange_rate) {
frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate);
}
frm.set_value("received_amount", frm.doc.paid_amount);
} else {
frm.events.received_amount(frm);
}
}
}
);
@@ -317,26 +325,31 @@ frappe.ui.form.on('Payment Entry', {
},
callback: function(r, rt) {
if(r.message) {
frm.set_value(currency_field, r.message['account_currency']);
frm.set_value(balance_field, r.message['account_balance']);
frappe.run_serially([
() => frm.set_value(currency_field, r.message['account_currency']),
() => {
frm.set_value(balance_field, r.message['account_balance']);
if(frm.doc.payment_type=="Receive" && currency_field=="paid_to_account_currency") {
frm.toggle_reqd(["reference_no", "reference_date"],
(r.message['account_type'] == "Bank" ? 1 : 0));
if(!frm.doc.received_amount && frm.doc.paid_amount)
frm.events.paid_amount(frm);
} else if(frm.doc.payment_type=="Pay" && currency_field=="paid_from_account_currency") {
frm.toggle_reqd(["reference_no", "reference_date"],
(r.message['account_type'] == "Bank" ? 1 : 0));
if(frm.doc.payment_type=="Receive" && currency_field=="paid_to_account_currency") {
frm.toggle_reqd(["reference_no", "reference_date"],
(r.message['account_type'] == "Bank" ? 1 : 0));
if(!frm.doc.received_amount && frm.doc.paid_amount)
frm.events.paid_amount(frm);
} else if(frm.doc.payment_type=="Pay" && currency_field=="paid_from_account_currency") {
frm.toggle_reqd(["reference_no", "reference_date"],
(r.message['account_type'] == "Bank" ? 1 : 0));
if(!frm.doc.paid_amount && frm.doc.received_amount)
frm.events.received_amount(frm);
}
if(!frm.doc.paid_amount && frm.doc.received_amount)
frm.events.received_amount(frm);
}
},
() => {
if(callback_function) callback_function(frm);
if(callback_function) callback_function(frm);
frm.events.hide_unhide_fields(frm);
frm.events.set_dynamic_labels(frm);
frm.events.hide_unhide_fields(frm);
frm.events.set_dynamic_labels(frm);
}
]);
}
}
});
@@ -405,7 +418,7 @@ frappe.ui.form.on('Payment Entry', {
frm.set_value("base_received_amount", frm.doc.base_paid_amount);
}
frm.events.set_difference_amount(frm);
frm.events.set_unallocated_amount(frm);
}
// Make read only if Accounts Settings doesn't allow stale rates
@@ -425,7 +438,7 @@ frappe.ui.form.on('Payment Entry', {
frm.set_value("base_paid_amount", frm.doc.base_received_amount);
}
frm.events.set_difference_amount(frm);
frm.events.set_unallocated_amount(frm);
}
frm.set_paid_amount_based_on_received_amount = false;
@@ -456,7 +469,7 @@ frappe.ui.form.on('Payment Entry', {
if(frm.doc.payment_type == "Pay")
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount);
else
frm.events.set_difference_amount(frm);
frm.events.set_unallocated_amount(frm);
frm.set_paid_amount_based_on_received_amount = false;
},
@@ -476,7 +489,7 @@ frappe.ui.form.on('Payment Entry', {
if(frm.doc.payment_type == "Receive")
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount);
else
frm.events.set_difference_amount(frm);
frm.events.set_unallocated_amount(frm);
},
get_outstanding_documents: function(frm) {
@@ -565,8 +578,11 @@ frappe.ui.form.on('Payment Entry', {
if(frm.doc.references.length == 0){
frm.events.get_outstanding_documents(frm);
}
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount);
if(frm.doc.payment_type == 'Internal Transfer') {
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount);
} else {
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount);
}
},
allocate_party_amount_against_ref_docs: function(frm, paid_amount) {
@@ -651,28 +667,32 @@ frappe.ui.form.on('Payment Entry', {
frm.set_value("total_allocated_amount", Math.abs(total_allocated_amount));
frm.set_value("base_total_allocated_amount", Math.abs(base_total_allocated_amount));
frm.events.set_difference_amount(frm);
frm.events.set_unallocated_amount(frm);
},
set_difference_amount: function(frm) {
set_unallocated_amount: function(frm) {
var unallocated_amount = 0;
var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [],
function(d) { return flt(d.amount) }));
if(frm.doc.party) {
var party_amount = frm.doc.payment_type=="Receive" ?
frm.doc.paid_amount : frm.doc.received_amount;
if(frm.doc.total_allocated_amount < party_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);
}
if(frm.doc.payment_type == "Receive"
&& frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions
&& frm.doc.total_allocated_amount < frm.doc.paid_amount + (total_deductions / frm.doc.source_exchange_rate)) {
unallocated_amount = (frm.doc.base_received_amount + total_deductions
- frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
} else if (frm.doc.payment_type == "Pay"
&& frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions
&& frm.doc.total_allocated_amount < frm.doc.received_amount + (total_deductions / frm.doc.target_exchange_rate)) {
unallocated_amount = (frm.doc.base_paid_amount - (total_deductions
+ frm.doc.base_total_allocated_amount)) / frm.doc.target_exchange_rate;
}
}
frm.set_value("unallocated_amount", unallocated_amount);
frm.trigger("set_difference_amount");
},
set_difference_amount: function(frm) {
var difference_amount = 0;
var base_unallocated_amount = flt(frm.doc.unallocated_amount) *
(frm.doc.payment_type=="Receive" ? frm.doc.source_exchange_rate : frm.doc.target_exchange_rate);
@@ -687,11 +707,18 @@ 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);
},
unallocated_amount: function(frm) {
frm.trigger("set_difference_amount");
},
check_mandatory_to_fetch: function(frm) {
$.each(["Company", "Party Type", "Party", "payment_type"], function(i, field) {
if(!frm.doc[frappe.model.scrub(field)]) {
@@ -771,7 +798,7 @@ frappe.ui.form.on('Payment Entry', {
row.amount = flt(row.amount) + flt(frm.doc.difference_amount);
refresh_field("deductions");
frm.events.set_difference_amount(frm);
frm.events.set_unallocated_amount(frm);
}
}
})
@@ -818,10 +845,10 @@ frappe.ui.form.on('Payment Entry Reference', {
frappe.ui.form.on('Payment Entry Deduction', {
amount: function(frm) {
frm.events.set_difference_amount(frm);
frm.events.set_unallocated_amount(frm);
},
deductions_remove: function(frm) {
frm.events.set_difference_amount(frm);
frm.events.set_unallocated_amount(frm);
}
})

View File

@@ -40,6 +40,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -71,6 +72,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 1,
"translatable": 0,
"unique": 0
},
{
@@ -102,6 +104,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -131,6 +134,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -162,6 +166,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -193,6 +198,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -224,6 +230,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -255,6 +262,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -288,6 +296,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -320,6 +329,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -349,6 +359,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -381,6 +392,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -411,6 +423,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -442,6 +455,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -474,6 +488,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -506,6 +521,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -538,6 +554,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -567,6 +584,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -599,6 +617,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -631,6 +650,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -663,6 +683,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -695,6 +716,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -727,6 +749,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -758,6 +781,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -790,6 +814,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -819,6 +844,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -851,6 +877,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -882,6 +909,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -914,6 +942,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -946,6 +975,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -978,6 +1008,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1010,6 +1041,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1041,6 +1073,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1072,6 +1105,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1104,6 +1138,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1134,6 +1169,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1163,6 +1199,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1188,12 +1225,13 @@
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1226,6 +1264,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1257,6 +1296,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1289,6 +1329,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1320,6 +1361,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1350,6 +1392,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1381,6 +1424,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1410,6 +1454,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1441,6 +1486,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1472,6 +1518,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1504,6 +1551,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1536,6 +1584,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1566,6 +1615,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1595,6 +1645,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1626,6 +1677,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1657,6 +1709,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1687,6 +1740,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1718,6 +1772,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1748,6 +1803,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1778,6 +1834,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
@@ -1791,7 +1848,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-08-31 11:20:37.578469",
"modified": "2018-02-19 16:58:23.899015",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@@ -20,6 +20,11 @@ class InvalidPaymentEntry(ValidationError):
class PaymentEntry(AccountsController):
def __init__(self, *args, **kwargs):
super(PaymentEntry, self).__init__(*args, **kwargs)
if not self.is_new():
self.setup_party_account_field()
def setup_party_account_field(self):
self.party_account_field = None
self.party_account = None
@@ -286,17 +291,19 @@ class PaymentEntry(AccountsController):
self.base_total_allocated_amount = abs(base_total_allocated_amount)
def set_unallocated_amount(self):
self.unallocated_amount = 0;
self.unallocated_amount = 0
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:
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)
if self.payment_type == "Receive" \
and self.base_total_allocated_amount < self.base_received_amount + total_deductions \
and self.total_allocated_amount < self.paid_amount + (total_deductions / self.source_exchange_rate):
self.unallocated_amount = (self.base_received_amount + total_deductions -
self.base_total_allocated_amount) / self.source_exchange_rate
elif self.payment_type == "Pay" \
and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) \
and self.total_allocated_amount < self.received_amount + (total_deductions / self.target_exchange_rate):
self.unallocated_amount = (self.base_paid_amount - (total_deductions +
self.base_total_allocated_amount)) / self.target_exchange_rate
def set_difference_amount(self):
base_unallocated_amount = flt(self.unallocated_amount) * (flt(self.source_exchange_rate)
@@ -665,6 +672,24 @@ def get_company_defaults(company):
return ret
def get_outstanding_on_journal_entry(name):
res = frappe.db.sql(
'SELECT '
'CASE WHEN party_type IN ("Customer", "Student") '
'THEN ifnull(sum(debit_in_account_currency - credit_in_account_currency), 0) '
'ELSE ifnull(sum(credit_in_account_currency - debit_in_account_currency), 0) '
'END as outstanding_amount '
'FROM `tabGL Entry` WHERE (voucher_no=%s OR against_voucher=%s) '
'AND party_type IS NOT NULL '
'AND party_type != ""',
(name, name), as_dict=1
)
outstanding_amount = res[0].get('outstanding_amount', 0) if res else 0
return outstanding_amount
@frappe.whitelist()
def get_reference_details(reference_doctype, reference_name, party_account_currency):
total_amount = outstanding_amount = exchange_rate = None
@@ -675,6 +700,13 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
total_amount = ref_doc.get("grand_total")
exchange_rate = 1
outstanding_amount = ref_doc.get("outstanding_amount")
elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1:
total_amount = ref_doc.get("total_amount")
if ref_doc.multi_currency:
exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
else:
exchange_rate = 1
outstanding_amount = get_outstanding_on_journal_entry(reference_name)
elif reference_doctype != "Journal Entry":
if party_account_currency == company_currency:
if ref_doc.doctype == "Expense Claim":

View File

@@ -141,7 +141,6 @@ class TestPaymentEntry(unittest.TestCase):
def test_payment_entry_retrieves_last_exchange_rate(self):
from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records, save_new_records
test_records = test_records
save_new_records(test_records)
pe = frappe.new_doc("Payment Entry")
@@ -151,6 +150,7 @@ class TestPaymentEntry(unittest.TestCase):
pe.paid_from = "_Test Bank USD - _TC"
pe.paid_to = "_Test Bank - _TC"
pe.paid_amount = 100
pe.received_amount = 100
pe.reference_no = "3"
pe.reference_date = "2016-01-10"
pe.party_type = "Supplier"

View File

@@ -98,7 +98,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-07-11 03:28:03.420683",
"modified": "2018-02-21 03:28:03.420683",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry Deduction",

View File

@@ -61,13 +61,11 @@ class TestPaymentRequest(unittest.TestCase):
self.assertEquals(pr.currency, "USD")
def test_payment_entry(self):
frappe.db.set_value("Company", "_Test Company",
frappe.db.set_value("Company", "_Test Company",
"exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC")
frappe.db.set_value("Company", "_Test Company",
"write_off_account", "_Test Write Off - _TC")
frappe.db.set_value("Company", "_Test Company",
"cost_center", "_Test Cost Center - _TC")
frappe.db.set_value("Company", "_Test Company", "write_off_account", "_Test Write Off - _TC")
frappe.db.set_value("Company", "_Test Company", "cost_center", "_Test Cost Center - _TC")
so_inr = make_sales_order(currency="INR")
pr = make_payment_request(dt="Sales Order", dn=so_inr.name, recipient_id="saurabh@erpnext.com",
mute_email=1, submit_doc=1, return_doc=1)
@@ -82,15 +80,15 @@ class TestPaymentRequest(unittest.TestCase):
pr = make_payment_request(dt="Sales Invoice", dn=si_usd.name, recipient_id="saurabh@erpnext.com",
mute_email=1, payment_gateway="_Test Gateway - USD", submit_doc=1, return_doc=1)
pe = pr.set_as_paid()
expected_gle = dict((d[0], d) for d in [
["_Test Receivable USD - _TC", 0, 5000, si_usd.name],
[pr.payment_account, 6290.0, 0, None],
["_Test Exchange Gain/Loss - _TC", 0, 1290, None]
])
gl_entries = frappe.db.sql("""select account, debit, credit, against_voucher
from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
order by account asc""", pe.name, as_dict=1)

View File

@@ -350,7 +350,6 @@ class PurchaseInvoice(BuyingController):
self.negative_expense_to_be_booked = 0.0
gl_entries = []
self.make_supplier_gl_entry(gl_entries)
self.make_item_gl_entries(gl_entries)
self.make_tax_gl_entries(gl_entries)
@@ -424,7 +423,10 @@ class PurchaseInvoice(BuyingController):
# sub-contracting warehouse
if flt(item.rm_supp_cost):
supplier_warehouse_account = warehouse_account[self.supplier_warehouse]["name"]
supplier_warehouse_account = warehouse_account[self.supplier_warehouse]["account"]
if not supplier_warehouse_account:
frappe.throw(_("Please set account in Warehouse {0}")
.format(self.supplier_warehouse))
gl_entries.append(self.get_gl_dict({
"account": supplier_warehouse_account,
"against": item.expense_account,

View File

@@ -106,7 +106,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
on_submit: function(doc, dt, dn) {
var me = this;
if (frappe.get_route()[0] != 'Sales Invoice') {
if (frappe.get_route()[0] != 'Form') {
return
}

View File

@@ -687,12 +687,12 @@
"in_standard_filter": 0,
"label": "Customer's Purchase Order",
"length": 0,
"no_copy": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
@@ -4683,7 +4683,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2018-01-12 15:19:54.711885",
"modified": "2018-03-13 15:19:54.711885",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@@ -101,6 +101,8 @@ class SalesInvoice(SellingController):
self.set_billing_hours_and_amount()
self.update_timesheet_billing_for_project()
self.set_status()
if self.is_pos and not self.is_return:
self.verify_payment_amount_is_positive()
def before_save(self):
set_account_for_mode_of_payment(self)
@@ -903,6 +905,11 @@ class SalesInvoice(SellingController):
project.update_billed_amount()
project.save()
def verify_payment_amount_is_positive(self):
for entry in self.payments:
if entry.amount < 0:
frappe.throw(_("Row #{0} (Payment Table): Amount must be positive").format(entry.idx))
def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context
list_context = get_list_context(context)

View File

@@ -227,6 +227,36 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "clearance_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": "Clearance Date",
"length": 0,
"no_copy": 0,
"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,
@@ -239,7 +269,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-07-24 17:25:03.765856",
"modified": "2018-03-07 18:34:39.552769",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Payment",

View File

@@ -393,7 +393,8 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.frm = {}
this.frm.doc = this.doc
this.set_transaction_defaults("Customer");
this.frm.doc["allow_user_to_edit_rate"] = this.pos_profile_data["allow_user_to_edit_rate"] ? true : false,
this.frm.doc["allow_user_to_edit_rate"] = this.pos_profile_data["allow_user_to_edit_rate"] ? true : false;
this.frm.doc["allow_user_to_edit_discount"] = this.pos_profile_data["allow_user_to_edit_discount"] ? true : false;
this.wrapper.html(frappe.render_template("pos", this.frm.doc));
this.make_search();
this.make_customer();
@@ -1256,6 +1257,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
$(this.wrapper).find('.selected-item').empty();
if(this.child_doc.length) {
this.child_doc[0]["allow_user_to_edit_rate"] = this.pos_profile_data["allow_user_to_edit_rate"] ? true : false,
this.child_doc[0]["allow_user_to_edit_discount"] = this.pos_profile_data["allow_user_to_edit_discount"] ? true : false;
this.selected_row = $(frappe.render_template("pos_selected_item", this.child_doc[0]))
$(this.wrapper).find('.selected-item').html(this.selected_row)
}
@@ -1683,7 +1685,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
setInterval(function () {
me.freeze_screen = false;
me.sync_sales_invoice()
}, 60000)
}, 180000)
},
sync_sales_invoice: function () {

View File

@@ -4,12 +4,11 @@
from __future__ import unicode_literals
import frappe
import datetime
from frappe import _, msgprint, scrub
from frappe.defaults import get_user_permissions
from frappe.model.utils import get_fetch_values
from frappe.utils import (add_days, getdate, formatdate, get_first_day, date_diff,
add_years, get_timestamp, nowdate, flt, add_months, get_last_day)
from frappe.utils import (add_days, getdate, formatdate, date_diff,
add_years, get_timestamp, nowdate, flt, add_months, get_last_day)
from frappe.contacts.doctype.address.address import (get_address_display,
get_default_address, get_company_address)
from frappe.contacts.doctype.contact.contact import get_contact_details, get_default_contact

View File

@@ -1,10 +1,15 @@
<style>
@media screen {
.print-format {
padding: 8mm;
}
}
</style>
{% if(filters.show_pdc_in_print) { %}
<style>
@media screen {
.print-format {
padding: 8mm;
margin:4mm;
font-size:10px;
font-family: Tahoma, sans-serif;
}
}
</style>
{% } %}
<h2 class="text-center">{%= __(report.report_name) %}</h2>
<h4 class="text-center">{%= filters.customer || filters.supplier %} </h4>
@@ -82,21 +87,24 @@
<tr>
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
<th style="width: 10%">{%= __("Date") %}</th>
<th style="width: 10%">{%= __("Ref") %}</th>
<th style="width: 15%">{%= __("Ref") %}</th>
{% if(!filters.show_pdc_in_print) { %}
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
{% } %}
<th style="width: 10%">{%= __("Invoiced Amount") %}</th>
{% if(!filters.show_pdc_in_print) { %}
<th style="width: 10%">{%= __("Paid Amount") %}</th>
<th style="width: 10%">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
{% } %}
<th style="width: 6%">{%= __("Outstanding Amount") %}</th>
<th style="width: 15%">{%= __("Outstanding Amount") %}</th>
{% if(filters.show_pdc_in_print) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<th style="width: 6%">{%= __("Customer LPO No.") %}</th>
<th style="width: 10%">{%= __("Customer LPO No.") %}</th>
{% } %}
<th style="width: 6%">{%= __("PDC/LC Date") %}</th>
<th style="width: 6%">{%= __("PDC/LC Ref") %}</th>
<th style="width: 6%">{%= __("PDC/LC Amount") %}</th>
<th style="width: 10%">{%= __("PDC/LC Date") %}</th>
<th style="width: 10%">{%= __("PDC/LC Ref") %}</th>
<th style="width: 10%">{%= __("PDC/LC Amount") %}</th>
<th style="width: 10%">{%= __("Remaining Balance") %}</th>
{% } %}
{% } else { %}
<th style="width: 40%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
@@ -115,6 +123,7 @@
<td>{%= dateutil.str_to_user(data[i][__("Posting Date")]) %}</td>
<td>{%= data[i][__("Voucher Type")] %}
<br>{%= data[i][__("Voucher No")] %}</td>
{% if(!filters.show_pdc_in_print) { %}
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{%= data[i][__("Customer")] || data[i][__("Supplier")] %}
@@ -127,6 +136,7 @@
<br>{%= __("Remarks") %}:
{%= data[i][__("Remarks")] %}
</td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["Invoiced Amount"], data[i]["currency"]) %}</td>
@@ -147,10 +157,13 @@
<td style="text-align: right">{%= frappe.datetime.str_to_user(data[i][__("PDC/LC Date")]) %}</td>
<td style="text-align: right">{%= data[i][__("PDC/LC Ref")] %}</td>
<td style="text-align: right">{%= format_currency(data[i][__("PDC/LC Amount")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][__("Remaining Balance")], data[i]["currency"]) %}</td>
{% } %}
{% } else { %}
<td></td>
{% if(!filters.show_pdc_in_print) { %}
<td></td>
{% } %}
<td><b>{%= __("Total") %}</b></td>
<td style="text-align: right">
{%= format_currency(data[i]["Invoiced Amount"], data[i]["currency"] ) %}</td>
@@ -171,6 +184,7 @@
<td style="text-align: right">{%= frappe.datetime.str_to_user(data[i][__("PDC/LC Date")]) %}</td>
<td style="text-align: right">{%= data[i][__("PDC/LC Ref")] %}</td>
<td style="text-align: right">{%= format_currency(data[i][__("PDC/LC Amount")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][__("Remaining Balance")], data[i]["currency"]) %}</td>
{% } %}
{% } %}
{% } else { %}

View File

@@ -159,7 +159,7 @@ class ReceivablePayableReport(object):
else:
row.append(company_currency)
pdc = pdc_details.get(gle.voucher_no, {})
pdc = pdc_details.get((gle.voucher_no, gle.party), {})
remaining_balance = outstanding_amount - flt(pdc.get("pdc_amount"))
row += [pdc.get("pdc_date"), pdc.get("pdc_ref"),
flt(pdc.get("pdc_amount")), remaining_balance]
@@ -389,25 +389,33 @@ def get_pdc_details(party_type):
on
(pref.parent = pent.name)
where
pent.docstatus = 0 and pent.reference_date > pent.posting_date
pent.docstatus < 2 and pent.reference_date >= pent.posting_date
and pent.party_type = %s
group by pref.reference_name""", party_type, as_dict=1):
pdc_details.setdefault(pdc.invoice_no, pdc)
group by pent.party, pref.reference_name""", party_type, as_dict=1):
pdc_details.setdefault((pdc.invoice_no, pdc.party), pdc)
if scrub(party_type):
amount_field = "jea.debit_in_account_currency + jea.credit_in_account_currency"
else:
amount_field = "jea.debit + jea.credit"
for pdc in frappe.db.sql("""
select
jea.reference_name as invoice_no, jea.party, jea.party_type,
max(je.cheque_date) as pdc_date, sum(ifnull(je.total_amount,0)) as pdc_amount,
max(je.cheque_date) as pdc_date, sum(ifnull({0},0)) as pdc_amount,
GROUP_CONCAT(je.cheque_no SEPARATOR ', ') as pdc_ref
from
`tabJournal Entry` as je inner join `tabJournal Entry Account` as jea
on
(jea.parent = je.name)
where
je.docstatus = 0 and je.cheque_date > je.posting_date
je.docstatus < 2 and je.cheque_date >= je.posting_date
and jea.party_type = %s
group by jea.reference_name""", party_type, as_dict=1):
pdc_details.setdefault(pdc.invoice_no, pdc)
group by jea.party, jea.reference_name""".format(amount_field), party_type, as_dict=1):
if (pdc.invoice_no, pdc.party) in pdc_details:
pdc_details[(pdc.invoice_no, pdc.party)]["pdc_amount"] += pdc.pdc_amount
else:
pdc_details.setdefault((pdc.invoice_no, pdc.party), pdc)
return pdc_details

View File

@@ -28,5 +28,10 @@ frappe.query_reports["Bank Reconciliation Statement"] = {
"default": frappe.datetime.get_today(),
"reqd": 1
},
{
"fieldname":"include_pos_transactions",
"label": __("Include POS Transactions"),
"fieldtype": "Check"
},
]
}
}

View File

@@ -138,7 +138,23 @@ def get_entries(filters):
and ifnull(clearance_date, '4000-01-01') > %(report_date)s
""", filters, as_dict=1)
return sorted(list(payment_entries)+list(journal_entries),
pos_entries = []
if filters.include_pos_transactions:
pos_entries = frappe.db.sql("""
select
"Sales Invoice Payment" as payment_document, sip.name as payment_entry, sip.amount as debit,
si.posting_date, si.debit_to as against_account, sip.clearance_date,
account.account_currency, 0 as credit
from `tabSales Invoice Payment` sip, `tabSales Invoice` si, `tabAccount` account
where
sip.account=%(account)s and si.docstatus=1 and sip.parent = si.name
and account.name = sip.account and si.posting_date <= %(report_date)s and
ifnull(sip.clearance_date, '4000-01-01') > %(report_date)s
order by
si.posting_date ASC, si.name DESC
""", filters, as_dict=1)
return sorted(list(payment_entries)+list(journal_entries+list(pos_entries)),
key=lambda k: k['posting_date'] or getdate(nowdate()))
def get_amounts_not_reflected_in_system(filters):

View File

@@ -105,7 +105,7 @@ def get_conditions(filters):
if filters.get("mode_of_payment"):
conditions += """ and exists(select name from `tabSales Invoice Payment`
where parent=si.name
where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)"""
return conditions

View File

@@ -5,6 +5,7 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe import _
class Crop(Document):
def validate(self):
@@ -12,7 +13,7 @@ class Crop(Document):
for task in self.agriculture_task:
# validate start_day is not > end_day
if task.start_day > task.end_day:
frappe.throw("Start day is greater than end day in task '{0}'".format(task.subject))
frappe.throw(_("Start day is greater than end day in task '{0}'").format(task.task_name))
# to calculate the period of the Crop Cycle
if task.end_day > max_period: max_period = task.end_day
if max_period > self.period: self.period = max_period

View File

@@ -5,6 +5,7 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe import _
class Disease(Document):
def validate(self):
@@ -12,7 +13,7 @@ class Disease(Document):
for task in self.treatment_task:
# validate start_day is not > end_day
if task.start_day > task.end_day:
frappe.throw("Start day is greater than end day in task '{0}'".format(task.task_name))
frappe.throw(_("Start day is greater than end day in task '{0}'").format(task.task_name))
# to calculate the period of the Crop Cycle
if task.end_day > max_period: max_period = task.end_day
self.treatment_period = max_period

View File

@@ -6,6 +6,7 @@ from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe.utils import flt, cint
from frappe import _
class SoilTexture(Document):
soil_edit_order = [2, 1, 0]
@@ -20,9 +21,9 @@ class SoilTexture(Document):
self.update_soil_edit('sand_composition')
for soil_type in self.soil_types:
if self.get(soil_type) > 100 or self.get(soil_type) < 0:
frappe.throw("{0} should be a value between 0 and 100".format(soil_type))
frappe.throw(_("{0} should be a value between 0 and 100").format(soil_type))
if sum(self.get(soil_type) for soil_type in self.soil_types) != 100:
frappe.throw('Soil compositions do not add up to 100')
frappe.throw(_('Soil compositions do not add up to 100'))
def update_soil_edit(self, soil_type):
self.soil_edit_order[self.soil_types.index(soil_type)] = max(self.soil_edit_order)+1

View File

@@ -5,6 +5,7 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe import _
class WaterAnalysis(Document):
def load_contents(self):
@@ -18,6 +19,6 @@ class WaterAnalysis(Document):
def validate(self):
if self.collection_datetime > self.laboratory_testing_datetime:
frappe.throw('Lab testing datetime cannot be before collection datetime')
frappe.throw(_('Lab testing datetime cannot be before collection datetime'))
if self.laboratory_testing_datetime > self.result_datetime:
frappe.throw('Lab result datetime cannot be before testing datetime')
frappe.throw(_('Lab result datetime cannot be before testing datetime'))

View File

@@ -15,6 +15,15 @@ frappe.ui.form.on("Purchase Order", {
frm.set_indicator_formatter('item_code',
function(doc) { return (doc.qty<=doc.received_qty) ? "green" : "orange" })
frm.set_query("reserve_warehouse", "supplied_items", function() {
return {
filters: {
"company": frm.doc.company,
"is_group": 0
}
}
});
},
onload: function(frm) {
@@ -134,23 +143,124 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
var items = $.map(cur_frm.doc.items, function(d) { return d.bom ? d.item_code : false; });
var me = this;
if(items.length===1) {
me._make_stock_entry(items[0]);
return;
if(items.length >= 1){
me.raw_material_data = [];
me.show_dialog = 1;
let title = "";
let fields = [
{fieldtype:'Section Break', label: __('Raw Materials')},
{fieldname: 'sub_con_rm_items', fieldtype: 'Table',
fields: [
{
fieldtype:'Data',
fieldname:'item_code',
label: __('Item'),
read_only:1,
in_list_view:1
},
{
fieldtype:'Data',
fieldname:'rm_item_code',
label: __('Raw Material'),
read_only:1,
in_list_view:1
},
{
fieldtype:'Float',
read_only:1,
fieldname:'qty',
label: __('Quantity'),
read_only:1,
in_list_view:1
},
{
fieldtype:'Data',
read_only:1,
fieldname:'warehouse',
label: __('Reserve Warehouse'),
in_list_view:1
},
{
fieldtype:'Float',
read_only:1,
fieldname:'rate',
label: __('Rate'),
hidden:1
},
{
fieldtype:'Float',
read_only:1,
fieldname:'amount',
label: __('Amount'),
hidden:1
},
{
fieldtype:'Link',
read_only:1,
fieldname:'uom',
label: __('UOM'),
hidden:1
}
],
data: me.raw_material_data,
get_data: function() {
return me.raw_material_data;
}
}
]
me.dialog = new frappe.ui.Dialog({
title: title, fields: fields
});
if (me.frm.doc['supplied_items']) {
me.frm.doc['supplied_items'].forEach((item, index) => {
if (item.rm_item_code && item.main_item_code) {
me.raw_material_data.push ({
'name':index,
'item_code': item.main_item_code,
'rm_item_code': item.rm_item_code,
'item_name': item.rm_item_code,
'qty': item.required_qty,
'warehouse':item.reserve_warehouse,
'rate':item.rate,
'amount':item.amount,
'stock_uom':item.stock_uom
});
me.dialog.fields_dict.sub_con_rm_items.grid.refresh();
}
})
}
frappe.prompt({fieldname:"item", options: items, fieldtype:"Select",
label: __("Select Item for Transfer"), reqd: 1}, function(data) {
me._make_stock_entry(data.item);
}, __("Select Item"), __("Make"));
me.dialog.show()
this.dialog.set_primary_action(__('Transfer'), function() {
me.values = me.dialog.get_values();
if(me.values) {
me.values.sub_con_rm_items.map((row,i) => {
if (!row.item_code || !row.rm_item_code || !row.warehouse || !row.qty || row.qty === 0) {
frappe.throw(__("Item Code, warehouse, quantity are required on row" + (i+1)));
}
})
me._make_rm_stock_entry(me.dialog.fields_dict.sub_con_rm_items.grid.get_selected_children())
me.dialog.hide()
}
});
}
me.dialog.get_close_btn().on('click', () => {
me.dialog.hide();
});
},
_make_stock_entry: function(item) {
_make_rm_stock_entry: function(rm_items) {
frappe.call({
method:"erpnext.buying.doctype.purchase_order.purchase_order.make_stock_entry",
method:"erpnext.buying.doctype.purchase_order.purchase_order.make_rm_stock_entry",
args: {
purchase_order: cur_frm.doc.name,
item_code: item
},
rm_items: rm_items
}
,
callback: function(r) {
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
@@ -284,7 +394,8 @@ cur_frm.fields_dict['items'].grid.get_field('bom').get_query = function(doc, cdt
filters: [
['BOM', 'item', '=', d.item_code],
['BOM', 'is_active', '=', '1'],
['BOM', 'docstatus', '=', '1']
['BOM', 'docstatus', '=', '1'],
['BOM', 'company', '=', doc.company]
]
}
}

View File

@@ -3133,6 +3133,38 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.is_subcontracted",
"fieldname": "supplied_items_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": "Supplied Items",
"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,
"depends_on": "",
"fieldname": "supplied_items",
"fieldtype": "Table",
"hidden": 0,

View File

@@ -12,7 +12,7 @@ from erpnext.stock.doctype.item.item import get_last_purchase_details
from erpnext.stock.stock_balance import update_bin_qty, get_ordered_qty
from frappe.desk.notifications import clear_doctype_notifications
from erpnext.buying.utils import validate_for_items, check_for_closed_status
from erpnext.stock.utils import get_bin
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@@ -30,8 +30,7 @@ class PurchaseOrder(BuyingController):
'target_parent_field': 'per_ordered',
'target_ref_field': 'stock_qty',
'source_field': 'stock_qty',
'percent_join_field': 'material_request',
'overflow_type': 'order'
'percent_join_field': 'material_request'
}]
def onload(self):
@@ -81,8 +80,10 @@ class PurchaseOrder(BuyingController):
def validate_supplier(self):
prevent_po = frappe.db.get_value("Supplier", self.supplier, 'prevent_pos')
if prevent_po:
standing = frappe.db.get_value("Supplier Scorecard",self.supplier, 'status')
frappe.throw(_("Purchase Orders are not allowed for {0} due to a scorecard standing of {1}.").format(self.supplier, standing))
standing = frappe.db.get_value("Supplier Scorecard", self.supplier, 'status')
if standing:
frappe.throw(_("Purchase Orders are not allowed for {0} due to a scorecard standing of {1}.")
.format(self.supplier, standing))
warn_po = frappe.db.get_value("Supplier", self.supplier, 'warn_pos')
if warn_po:
@@ -193,6 +194,9 @@ class PurchaseOrder(BuyingController):
self.set_status(update=True, status=status)
self.update_requested_qty()
self.update_ordered_qty()
if self.is_subcontracted == "Yes":
self.update_reserved_qty_for_subcontract()
self.notify_update()
clear_doctype_notifications(self)
@@ -205,6 +209,8 @@ class PurchaseOrder(BuyingController):
self.update_prevdoc_status()
self.update_requested_qty()
self.update_ordered_qty()
if self.is_subcontracted == "Yes":
self.update_reserved_qty_for_subcontract()
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
self.company, self.base_grand_total)
@@ -218,6 +224,9 @@ class PurchaseOrder(BuyingController):
if self.has_drop_ship_item():
self.update_delivered_qty_in_sales_order()
if self.is_subcontracted == "Yes":
self.update_reserved_qty_for_subcontract()
self.check_for_closed_status()
frappe.db.set(self,'status','Cancelled')
@@ -232,12 +241,16 @@ class PurchaseOrder(BuyingController):
pass
def update_status_updater(self):
self.status_updater[0].update({
"target_parent_dt": "Sales Order",
"target_dt": "Sales Order Item",
self.status_updater.append({
'source_dt': 'Purchase Order Item',
'target_dt': 'Sales Order Item',
'target_field': 'ordered_qty',
"join_field": "sales_order_item",
"target_parent_field": ''
'target_parent_dt': 'Sales Order',
'target_parent_field': '',
'join_field': 'sales_order_item',
'source_dt': 'Purchase Order Item',
'target_ref_field': 'stock_qty',
'source_field': 'stock_qty'
})
def update_delivered_qty_in_sales_order(self):
@@ -265,6 +278,12 @@ class PurchaseOrder(BuyingController):
if item.delivered_by_supplier == 1:
item.received_qty = item.qty
def update_reserved_qty_for_subcontract(self):
for d in self.supplied_items:
if d.rm_item_code:
stock_bin = get_bin(d.rm_item_code, d.reserve_warehouse)
stock_bin.update_reserved_qty_for_sub_contracting()
def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor= 1.0):
"""get last purchase rate for an item"""
if cint(frappe.db.get_single_value("Buying Settings", "disable_fetch_last_purchase_rate")): return
@@ -385,23 +404,52 @@ def make_purchase_invoice(source_name, target_doc=None):
return doc
@frappe.whitelist()
def make_stock_entry(purchase_order, item_code):
purchase_order = frappe.get_doc("Purchase Order", purchase_order)
def make_rm_stock_entry(purchase_order, rm_items):
if isinstance(rm_items, basestring):
rm_items_list = json.loads(rm_items)
else:
frappe.throw(_("No Items available for transfer"))
stock_entry = frappe.new_doc("Stock Entry")
stock_entry.purpose = "Subcontract"
stock_entry.purchase_order = purchase_order.name
stock_entry.supplier = purchase_order.supplier
stock_entry.supplier_name = purchase_order.supplier_name
stock_entry.supplier_address = purchase_order.supplier_address
stock_entry.address_display = purchase_order.address_display
stock_entry.company = purchase_order.company
stock_entry.from_bom = 1
po_item = [d for d in purchase_order.items if d.item_code == item_code][0]
stock_entry.fg_completed_qty = po_item.qty
stock_entry.bom_no = po_item.bom
stock_entry.get_items()
return stock_entry.as_dict()
if rm_items_list:
fg_items = list(set(d["item_code"] for d in rm_items_list))
else:
frappe.throw(_("No Items selected for transfer"))
if purchase_order:
purchase_order = frappe.get_doc("Purchase Order", purchase_order)
if fg_items:
items = tuple(set(d["rm_item_code"] for d in rm_items_list))
item_wh = frappe._dict(frappe.db.sql("""
select item_code, description
from `tabItem` where name in ({0})
""".format(", ".join(["%s"] * len(items))), items))
stock_entry = frappe.new_doc("Stock Entry")
stock_entry.purpose = "Subcontract"
stock_entry.purchase_order = purchase_order.name
stock_entry.supplier = purchase_order.supplier
stock_entry.supplier_name = purchase_order.supplier_name
stock_entry.supplier_address = purchase_order.supplier_address
stock_entry.address_display = purchase_order.address_display
stock_entry.company = purchase_order.company
for item_code in fg_items:
for rm_item_data in rm_items_list:
if rm_item_data["item_code"] == item_code:
items_dict = {
rm_item_data["rm_item_code"]: {
"item_name":rm_item_data["item_name"],
"description":item_wh.get(rm_item_data["rm_item_code"]),
'qty':rm_item_data["qty"],
'from_warehouse':rm_item_data["warehouse"],
'stock_uom':rm_item_data["stock_uom"]
}
}
stock_entry.add_to_stock_entry_detail(items_dict)
return stock_entry.as_dict()
else:
frappe.throw(_("No Items selected for transfer"))
return purchase_order.name
@frappe.whitelist()
def update_status(status, name):

View File

@@ -6,8 +6,9 @@ import unittest
import frappe
import frappe.defaults
from frappe.utils import flt, add_days, nowdate
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt, make_purchase_invoice
from erpnext.buying.doctype.purchase_order.purchase_order import (make_purchase_receipt, make_purchase_invoice, make_rm_stock_entry as make_subcontract_transfer_entry)
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
import json
class TestPurchaseOrder(unittest.TestCase):
def test_make_purchase_receipt(self):
@@ -182,24 +183,129 @@ class TestPurchaseOrder(unittest.TestCase):
pi.insert()
self.assertTrue(pi.get('payment_schedule'))
def test_reserved_qty_subcontract_po(self):
# Make stock available for raw materials
make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100)
make_stock_entry(target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100",
qty=20, basic_rate=100)
bin1 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname=["reserved_qty_for_sub_contract", "projected_qty"], as_dict=1)
# Submit PO
po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
bin2 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname=["reserved_qty_for_sub_contract", "projected_qty"], as_dict=1)
self.assertEquals(bin2.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10)
self.assertEquals(bin2.projected_qty, bin1.projected_qty - 10)
# Create stock transfer
rm_item = [{"item_code":"_Test FG Item","rm_item_code":"_Test Item","item_name":"_Test Item",
"qty":6,"warehouse":"_Test Warehouse - _TC","rate":100,"amount":600,"stock_uom":"Nos"}]
rm_item_string = json.dumps(rm_item)
se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
se.to_warehouse = "_Test Warehouse 1 - _TC"
se.save()
se.submit()
bin3 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin3.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
# close PO
po.update_status("Closed")
bin4 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin4.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
# Re-open PO
po.update_status("Submitted")
bin5 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin5.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
# make Purchase Receipt against PO
pr = make_purchase_receipt(po.name)
pr.supplier_warehouse = "_Test Warehouse 1 - _TC"
pr.save()
pr.submit()
bin6 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin6.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
# Cancel PR
pr.cancel()
bin7 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin7.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
# Make Purchase Invoice
pi = make_purchase_invoice(po.name)
pi.update_stock = 1
pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
pi.insert()
pi.submit()
bin8 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin8.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
# Cancel PR
pi.cancel()
bin9 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin9.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
# Cancel Stock Entry
se.cancel()
bin10 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin10.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10)
# Cancel PO
po.reload()
po.cancel()
bin11 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin11.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
def get_same_items():
return [
{
"item_code": "_Test FG Item",
"warehouse": "_Test Warehouse - _TC",
"qty": 1,
"rate": 500,
"schedule_date": add_days(nowdate(), 1)
},
{
"item_code": "_Test FG Item",
"warehouse": "_Test Warehouse - _TC",
"qty": 4,
"rate": 500,
"schedule_date": add_days(nowdate(), 1)
}
]
{
"item_code": "_Test FG Item",
"warehouse": "_Test Warehouse - _TC",
"qty": 1,
"rate": 500,
"schedule_date": add_days(nowdate(), 1)
},
{
"item_code": "_Test FG Item",
"warehouse": "_Test Warehouse - _TC",
"qty": 4,
"rate": 500,
"schedule_date": add_days(nowdate(), 1)
}
]
def create_purchase_order(**args):
po = frappe.new_doc("Purchase Order")
@@ -224,6 +330,10 @@ def create_purchase_order(**args):
if not args.do_not_save:
po.insert()
if not args.do_not_submit:
if po.is_subcontracted == "Yes":
supp_items = po.get("supplied_items")
for d in supp_items:
d.reserve_warehouse = args.warehouse or "_Test Warehouse - _TC"
po.submit()
return po

View File

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
@@ -10,68 +11,86 @@
"editable_grid": 1,
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "main_item_code",
"fieldtype": "Data",
"fieldtype": "Link",
"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": "Item Code",
"length": 0,
"no_copy": 0,
"oldfieldname": "main_item_code",
"oldfieldtype": "Data",
"options": "Item",
"permlevel": 0,
"print_hide": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "rm_item_code",
"fieldtype": "Data",
"fieldtype": "Link",
"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": "Raw Material Item Code",
"length": 0,
"no_copy": 0,
"oldfieldname": "rm_item_code",
"oldfieldtype": "Data",
"options": "Item",
"permlevel": 0,
"print_hide": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "required_qty",
"fieldtype": "Float",
"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": "Supplied Qty",
"length": 0,
"no_copy": 0,
@@ -81,23 +100,29 @@
"print_hide": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "rate",
"fieldtype": "Currency",
"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": "Rate",
"length": 0,
"no_copy": 0,
@@ -108,23 +133,29 @@
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amount",
"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": "Amount",
"length": 0,
"no_copy": 0,
@@ -135,23 +166,59 @@
"print_hide": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_6",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bom_detail_no",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "BOM Detail No",
"length": 0,
"no_copy": 0,
@@ -161,23 +228,29 @@
"print_hide": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "reference_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Reference Name",
"length": 0,
"no_copy": 0,
@@ -187,23 +260,29 @@
"print_hide": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "conversion_factor",
"fieldtype": "Float",
"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": "Conversion Factor",
"length": 0,
"no_copy": 0,
@@ -213,23 +292,29 @@
"print_hide": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "stock_uom",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Stock Uom",
"length": 0,
"no_copy": 0,
@@ -240,24 +325,58 @@
"print_hide": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "reserve_warehouse",
"fieldtype": "Link",
"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": "Reserve Warehouse",
"length": 0,
"no_copy": 0,
"options": "Warehouse",
"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,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 1,
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-07-11 03:28:05.533063",
"modified": "2018-03-13 12:37:57.150914",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item Supplied",
@@ -266,5 +385,7 @@
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"track_changes": 0,
"track_seen": 0
}

View File

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
@@ -11,12 +12,13 @@
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "main_item_code",
"fieldtype": "Data",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -29,6 +31,7 @@
"no_copy": 0,
"oldfieldname": "main_item_code",
"oldfieldtype": "Data",
"options": "Item",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@@ -38,15 +41,17 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rm_item_code",
"fieldtype": "Data",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -59,6 +64,7 @@
"no_copy": 0,
"oldfieldname": "rm_item_code",
"oldfieldtype": "Data",
"options": "Item",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@@ -68,9 +74,11 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -99,10 +107,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "300px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -129,9 +139,11 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -157,9 +169,11 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -184,9 +198,11 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -214,9 +230,11 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -244,9 +262,11 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -275,9 +295,11 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -306,9 +328,11 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -337,9 +361,11 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -367,9 +393,11 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -397,9 +425,11 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -427,9 +457,11 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -457,20 +489,21 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-02-17 16:43:21.668443",
"modified": "2018-03-13 12:38:40.807453",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Receipt Item Supplied",

View File

@@ -126,6 +126,11 @@ def get_data():
"link": "Tree/Sales Person",
"description": _("Manage Sales Person Tree."),
},
{
"type": "doctype",
"name": "Lead Source",
"description": _("Track Leads by Lead Source.")
},
]
},
{

View File

@@ -200,7 +200,7 @@ def get_data():
"color": "#fd784f",
"icon": "octicon octicon-calendar",
"label": _("Course Schedule"),
"link": "Calendar/Course Schedule",
"link": "List/Course Schedule/Calendar",
"_doctype": "Course Schedule",
"type": "list",
"hidden": 1

View File

@@ -173,6 +173,11 @@ def get_data():
"name": "Industry Type",
"description": _("Track Leads by Industry Type.")
},
{
"type": "doctype",
"name": "Lead Source",
"description": _("Track Leads by Lead Source.")
},
]
},
{

View File

@@ -666,6 +666,8 @@ class AccountsController(TransactionBase):
self.remove(item)
def set_payment_schedule(self):
if self.doctype == 'Sales Invoice' and self.is_pos: return
posting_date = self.get("bill_date") or self.get("posting_date") or self.get("transaction_date")
date = self.get("due_date")
due_date = date or posting_date
@@ -695,6 +697,8 @@ class AccountsController(TransactionBase):
dates = []
li = []
if self.doctype == 'Sales Invoice' and self.is_pos: return
for d in self.get("payment_schedule"):
if self.doctype == "Sales Order" and getdate(d.due_date) < getdate(self.transaction_date):
frappe.throw(_("Row {0}: Due Date cannot be before posting date").format(d.idx))
@@ -708,6 +712,8 @@ class AccountsController(TransactionBase):
.format(list=duplicates))
def validate_payment_schedule_amount(self):
if self.doctype == 'Sales Invoice' and self.is_pos: return
if self.get("payment_schedule"):
total = 0
for d in self.get("payment_schedule"):

View File

@@ -163,6 +163,11 @@ class BuyingController(StockController):
if item in self.sub_contracted_items and not item.bom:
frappe.throw(_("Please select BOM in BOM field for Item {0}").format(item.item_code))
if self.doctype == "Purchase Order":
for supplied_item in self.get("supplied_items"):
if not supplied_item.reserve_warehouse:
frappe.throw(_("Reserved Warehouse is mandatory for Item {0} in Raw Materials supplied").format(frappe.bold(supplied_item.rm_item_code)))
else:
for item in self.get("items"):
if item.bom:
@@ -192,8 +197,16 @@ class BuyingController(StockController):
def update_raw_materials_supplied(self, item, raw_material_table):
bom_items = self.get_items_from_bom(item.item_code, item.bom)
raw_materials_cost = 0
items = list(set([d.item_code for d in bom_items]))
item_wh = frappe._dict(frappe.db.sql("""select item_code, default_warehouse
from `tabItem` where name in ({0})""".format(", ".join(["%s"] * len(items))), items))
for bom_item in bom_items:
if self.doctype == "Purchase Order":
reserve_warehouse = bom_item.source_warehouse or item_wh.get(bom_item.item_code)
if frappe.db.get_value("Warehouse", reserve_warehouse, "company") != self.company:
reserve_warehouse = None
# check if exists
exists = 0
for d in self.get(raw_material_table):
@@ -213,6 +226,8 @@ class BuyingController(StockController):
rm.rm_item_code = bom_item.item_code
rm.stock_uom = bom_item.stock_uom
rm.required_qty = required_qty
if self.doctype == "Purchase Order" and not rm.reserve_warehouse:
rm.reserve_warehouse = reserve_warehouse
rm.conversion_factor = item.conversion_factor
@@ -264,7 +279,7 @@ class BuyingController(StockController):
def get_items_from_bom(self, item_code, bom):
bom_items = frappe.db.sql("""select t2.item_code,
t2.stock_qty / ifnull(t1.quantity, 1) as qty_consumed_per_unit,
t2.rate, t2.stock_uom, t2.name, t2.description
t2.rate, t2.stock_uom, t2.name, t2.description, t2.source_warehouse
from `tabBOM` t1, `tabBOM Item` t2, tabItem t3
where t2.parent = t1.name and t1.item = %s
and t1.docstatus = 1 and t1.is_active = 1 and t1.name = %s
@@ -339,7 +354,7 @@ class BuyingController(StockController):
frappe.get_meta(item_row.doctype).get_label(fieldname), item_row['item_code'])))
def update_stock_ledger(self, allow_negative_stock=False, via_landed_cost_voucher=False):
self.update_ordered_qty()
self.update_ordered_and_reserved_qty()
sl_entries = []
stock_items = self.get_stock_items()
@@ -381,7 +396,7 @@ class BuyingController(StockController):
self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock,
via_landed_cost_voucher=via_landed_cost_voucher)
def update_ordered_qty(self):
def update_ordered_and_reserved_qty(self):
po_map = {}
for d in self.get("items"):
if self.doctype=="Purchase Receipt" \
@@ -400,6 +415,8 @@ class BuyingController(StockController):
frappe.InvalidStatusError)
po_obj.update_ordered_qty(po_item_rows)
if self.is_subcontracted:
po_obj.update_reserved_qty_for_subcontract()
def make_sl_entries_for_supplier_warehouse(self, sl_entries):
if hasattr(self, 'supplied_items'):

View File

@@ -92,7 +92,10 @@ def validate_is_incremental(numeric_attribute, attribute, value, item):
InvalidItemAttributeValueError, title=_('Invalid Attribute'))
def validate_item_attribute_value(attributes_list, attribute, attribute_value, item):
if attribute_value not in attributes_list:
allow_rename_attribute_value = frappe.db.get_single_value('Item Variant Settings', 'allow_rename_attribute_value')
if allow_rename_attribute_value:
pass
elif attribute_value not in attributes_list:
frappe.throw(_("Value {0} for Attribute {1} does not exist in the list of valid Item Attribute Values for Item {2}").format(
attribute_value, attribute, item), InvalidItemAttributeValueError, title=_('Invalid Attribute'))

View File

@@ -53,8 +53,9 @@ def validate_returned_items(doc):
valid_items = frappe._dict()
select_fields = "item_code, qty, rate, parenttype" if doc.doctype=="Purchase Invoice" \
else "item_code, qty, rate, serial_no, batch_no, parenttype"
select_fields = "item_code, qty, stock_qty, rate, parenttype, conversion_factor"
if doc.doctype != 'Purchase Invoice':
select_fields += ",serial_no, batch_no"
if doc.doctype in ['Purchase Invoice', 'Purchase Receipt']:
select_fields += ",rejected_qty, received_qty"
@@ -111,7 +112,7 @@ def validate_returned_items(doc):
frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
def validate_quantity(doc, args, ref, valid_items, already_returned_items):
fields = ['qty']
fields = ['stock_qty']
if doc.doctype in ['Purchase Receipt', 'Purchase Invoice']:
fields.extend(['received_qty', 'rejected_qty'])
@@ -119,16 +120,19 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items):
for column in fields:
returned_qty = flt(already_returned_data.get(column, 0)) if len(already_returned_data) > 0 else 0
reference_qty = ref.get(column)
reference_qty = (ref.get(column) if column == 'stock_qty'
else ref.get(column) * ref.get("conversion_factor", 1.0))
max_returnable_qty = flt(reference_qty) - returned_qty
label = column.replace('_', ' ').title()
if reference_qty:
if flt(args.get(column)) > 0:
frappe.throw(_("{0} must be negative in return document").format(label))
elif returned_qty >= reference_qty and args.get(column):
frappe.throw(_("Item {0} has already been returned")
.format(args.item_code), StockOverReturnError)
elif abs(args.get(column)) > max_returnable_qty:
elif (abs(args.get(column)) * args.get("conversion_factor", 1.0)) > max_returnable_qty:
frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}")
.format(args.idx, reference_qty, args.item_code), StockOverReturnError)
@@ -138,6 +142,7 @@ def get_ref_item_dict(valid_items, ref_item_row):
valid_items.setdefault(ref_item_row.item_code, frappe._dict({
"qty": 0,
"rate": 0,
"stock_qty": 0,
"rejected_qty": 0,
"received_qty": 0,
"serial_no": [],
@@ -145,6 +150,7 @@ def get_ref_item_dict(valid_items, ref_item_row):
}))
item_dict = valid_items[ref_item_row.item_code]
item_dict["qty"] += ref_item_row.qty
item_dict["stock_qty"] += ref_item_row.get('stock_qty', 0)
if ref_item_row.get("rate", 0) > item_dict["rate"]:
item_dict["rate"] = ref_item_row.get("rate", 0)
@@ -161,9 +167,10 @@ def get_ref_item_dict(valid_items, ref_item_row):
return valid_items
def get_already_returned_items(doc):
column = 'child.item_code, sum(abs(child.qty)) as qty'
column = 'child.item_code, sum(abs(child.qty)) as qty, sum(abs(child.stock_qty)) as stock_qty'
if doc.doctype in ['Purchase Invoice', 'Purchase Receipt']:
column += ', sum(abs(child.rejected_qty)) as rejected_qty, sum(abs(child.received_qty)) as received_qty'
column += """, sum(abs(child.rejected_qty) * child.conversion_factor) as rejected_qty,
sum(abs(child.received_qty) * child.conversion_factor) as received_qty"""
data = frappe.db.sql("""
select {0}
@@ -180,6 +187,7 @@ def get_already_returned_items(doc):
for d in data:
items.setdefault(d.item_code, frappe._dict({
"qty": d.get("qty"),
"stock_qty": d.get("stock_qty"),
"received_qty": d.get("received_qty"),
"rejected_qty": d.get("rejected_qty")
}))
@@ -231,6 +239,7 @@ def make_return_doc(doctype, source_name, target_doc=None):
target_doc.received_qty = -1* source_doc.received_qty
target_doc.rejected_qty = -1* source_doc.rejected_qty
target_doc.qty = -1* source_doc.qty
target_doc.stock_qty = -1 * source_doc.stock_qty
target_doc.purchase_order = source_doc.purchase_order
target_doc.purchase_order_item = source_doc.purchase_order_item
target_doc.rejected_warehouse = source_doc.rejected_warehouse
@@ -238,6 +247,7 @@ def make_return_doc(doctype, source_name, target_doc=None):
target_doc.received_qty = -1* source_doc.received_qty
target_doc.rejected_qty = -1* source_doc.rejected_qty
target_doc.qty = -1* source_doc.qty
target_doc.stock_qty = -1 * source_doc.stock_qty
target_doc.purchase_order = source_doc.purchase_order
target_doc.purchase_receipt = source_doc.purchase_receipt
target_doc.rejected_warehouse = source_doc.rejected_warehouse

View File

@@ -38,6 +38,7 @@ class SellingController(StockController):
self.validate_max_discount()
self.validate_selling_price()
self.set_qty_as_per_stock_uom()
self.set_po_nos()
check_active_sales_items(self)
def set_missing_values(self, for_validate=False):
@@ -326,9 +327,16 @@ class SellingController(StockController):
"actual_qty": -1*flt(d.qty),
"incoming_rate": return_rate
}))
self.make_sl_entries(sl_entries)
def set_po_nos(self):
if self.doctype in ("Delivery Note", "Sales Invoice"):
ref_fieldname = "against_sales_order" if self.doctype == "Delivery Note" else "sales_order"
sales_orders = list(set([d.get(ref_fieldname) for d in self.items]))
if sales_orders:
po_nos = frappe.get_all('Sales Order', 'po_no', filters = {'name': ('in', sales_orders)})
self.po_no = ', '.join(list(set([d.po_no for d in po_nos if d.po_no])))
def check_active_sales_items(obj):
for d in obj.get("items"):
if d.item_code:

View File

@@ -156,6 +156,9 @@ class StatusUpdater(Document):
# get unique transactions to update
for d in self.get_all_children():
if hasattr(d, 'qty') and d.qty < 0 and not self.get('is_return'):
frappe.throw(_("For an item {0}, quantity must be positive number").format(d.item_code))
if d.doctype == args['source_dt'] and d.get(args["join_field"]):
args['name'] = d.get(args['join_field'])
@@ -250,7 +253,7 @@ class StatusUpdater(Document):
if args['detail_id']:
if not args.get("extra_cond"): args["extra_cond"] = ""
frappe.db.sql("""update `tab%(target_dt)s`
set %(target_field)s = (
(select ifnull(sum(%(source_field)s), 0)
@@ -275,7 +278,7 @@ class StatusUpdater(Document):
"""Update percent field in parent transaction"""
self._update_modified(args, update_modified)
if args.get('target_parent_field'):
frappe.db.sql("""update `tab%(target_parent_dt)s`
set %(target_parent_field)s = round(

View File

@@ -38,9 +38,6 @@ frappe.ui.form.on('Fee Schedule', {
if (data.reload && data.reload === 1) {
frm.reload_doc();
}
if (data.progress && data.progress === "0") {
frappe.msgprint(__("Fee records will be created in the background. In case of any error the error message will be updated in the Schedule."));
}
if (data.progress) {
let progress_bar = $(cur_frm.dashboard.progress_area).find(".progress-bar");
if (progress_bar) {
@@ -74,6 +71,15 @@ frappe.ui.form.on('Fee Schedule', {
});
}, "fa fa-play", "btn-success");
}
if (frm.doc.fee_creation_status == "Successful") {
frm.add_custom_button(__("View Fees Records"), function() {
frappe.route_options = {
fee_schedule: frm.doc.name
};
frappe.set_route("List", "Fees");
});
}
},
fee_structure: function(frm) {

View File

@@ -4,7 +4,7 @@
"allow_import": 1,
"allow_rename": 0,
"autoname": "naming_series:",
"beta": 1,
"beta": 0,
"creation": "2017-07-18 15:21:21.527136",
"custom": 0,
"docstatus": 0,
@@ -1029,7 +1029,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-12-04 13:08:27.727709",
"modified": "2018-02-26 13:59:36.560780",
"modified_by": "Administrator",
"module": "Education",
"name": "Fee Schedule",

View File

@@ -56,8 +56,15 @@ class FeeSchedule(Document):
self.db_set("fee_creation_status", "In Process")
frappe.publish_realtime("fee_schedule_progress",
{"progress": "0", "reload": 1}, user=frappe.session.user)
enqueue(generate_fee, queue='default', timeout=6000, event='generate_fee',
fee_schedule=self.name)
total_records = sum([int(d.total_students) for d in self.student_groups])
if total_records > 10:
frappe.msgprint(_('''Fee records will be created in the background.
In case of any error the error message will be updated in the Schedule.'''))
enqueue(generate_fee, queue='default', timeout=6000, event='generate_fee',
fee_schedule=self.name)
else:
generate_fee(self.name)
def generate_fee(fee_schedule):
doc = frappe.get_doc("Fee Schedule", fee_schedule)
@@ -69,10 +76,7 @@ def generate_fee(fee_schedule):
frappe.throw(_("Please setup Students under Student Groups"))
for d in doc.student_groups:
students = frappe.db.sql(""" select sg.program, sg.batch, sgs.student, sgs.student_name
from `tabStudent Group` sg, `tabStudent Group Student` sgs
where sg.name=%s and sg.name=sgs.parent and sgs.active=1""", d.student_group, as_dict=1)
students = get_students(d.student_group, doc.academic_year, doc.academic_term, doc.student_category)
for student in students:
try:
fees_doc = get_mapped_doc("Fee Schedule", fee_schedule, {
@@ -86,7 +90,7 @@ def generate_fee(fee_schedule):
fees_doc.student = student.student
fees_doc.student_name = student.student_name
fees_doc.program = student.program
fees_doc.student_batch = student.batch
fees_doc.student_batch = student.student_batch_name
fees_doc.send_payment_request = doc.send_email
fees_doc.save()
fees_doc.submit()
@@ -110,6 +114,30 @@ def generate_fee(fee_schedule):
{"progress": "100", "reload": 1}, user=frappe.session.user)
def get_students(student_group, academic_year, academic_term=None, student_category=None):
conditions = ""
if student_category:
conditions = " and pe.student_category='{}'".format(frappe.db.escape(student_category))
if academic_term:
conditions = " and pe.academic_term='{}'".format(frappe.db.escape(academic_term))
students = frappe.db.sql("""
select pe.student, pe.student_name, pe.program, pe.student_batch_name
from `tabStudent Group Student` sgs, `tabProgram Enrollment` pe
where
pe.student = sgs.student and pe.academic_year = %s
and sgs.parent = %s and sgs.active = 1
{conditions}
""".format(conditions=conditions), (academic_year, student_group), as_dict=1)
return students
@frappe.whitelist()
def get_total_students(student_group, academic_year, academic_term=None, student_category=None):
total_students = get_students(student_group, academic_year, academic_term, student_category)
return len(total_students)
@frappe.whitelist()
def get_fee_structure(source_name,target_doc=None):
fee_request = get_mapped_doc("Fee Structure", source_name,
@@ -117,23 +145,3 @@ def get_fee_structure(source_name,target_doc=None):
"doctype": "Fee Schedule"
}}, ignore_permissions=True)
return fee_request
@frappe.whitelist()
def get_total_students(student_group, academic_year, academic_term=None, student_category=None):
conditions = ""
if student_category:
conditions = " and pe.student_category='{}'".format(frappe.db.escape(student_category))
if academic_term:
conditions = " and pe.academic_term='{}'".format(frappe.db.escape(academic_term))
return frappe.db.sql("""
select count(pe.name)
from `tabStudent Group Student` sgs, `tabProgram Enrollment` pe
where
pe.student = sgs.student
and pe.academic_year = %s
and sgs.parent = %s
and sgs.active = 1
{conditions}
""".format(conditions=conditions), (academic_year, student_group))[0][0]

View File

@@ -2,7 +2,7 @@
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"allow_rename": 1,
"autoname": "GARD.####",
"beta": 0,
"creation": "2016-07-21 15:32:51.163292",
@@ -507,7 +507,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-12-06 18:17:38.090252",
"modified": "2018-03-07 12:51:06.941609",
"modified_by": "Administrator",
"module": "Education",
"name": "Guardian",

View File

@@ -27,26 +27,27 @@ def execute(filters=None):
course_dict = values.get("course_dict")
for student in args.students:
student_row = {}
student_row["student"] = student
student_row["student_name"] = student_details[student]
for course in course_dict:
scrub_course = frappe.scrub(course)
if assessment_group in assessment_result[student][course]:
student_row["grade_" + scrub_course] = assessment_result[student][course][assessment_group]["Total Score"]["grade"]
student_row["score_" + scrub_course] = assessment_result[student][course][assessment_group]["Total Score"]["score"]
if student_details.get(student):
student_row = {}
student_row["student"] = student
student_row["student_name"] = student_details[student]
for course in course_dict:
scrub_course = frappe.scrub(course)
if assessment_group in assessment_result[student][course]:
student_row["grade_" + scrub_course] = assessment_result[student][course][assessment_group]["Total Score"]["grade"]
student_row["score_" + scrub_course] = assessment_result[student][course][assessment_group]["Total Score"]["score"]
# create the list of possible grades
if student_row["grade_" + scrub_course] not in grades:
grades.append(student_row["grade_" + scrub_course])
# create the list of possible grades
if student_row["grade_" + scrub_course] not in grades:
grades.append(student_row["grade_" + scrub_course])
# create the dict of for gradewise analysis
if student_row["grade_" + scrub_course] not in course_wise_analysis[course]:
course_wise_analysis[course][student_row["grade_" + scrub_course]] = 1
else:
course_wise_analysis[course][student_row["grade_" + scrub_course]] += 1
# create the dict of for gradewise analysis
if student_row["grade_" + scrub_course] not in course_wise_analysis[course]:
course_wise_analysis[course][student_row["grade_" + scrub_course]] = 1
else:
course_wise_analysis[course][student_row["grade_" + scrub_course]] += 1
data.append(student_row)
data.append(student_row)
course_list = [d for d in course_dict]
columns = get_column(course_dict)

View File

@@ -23,11 +23,6 @@ frappe.ui.form.on('Consultation', {
patient: frm.doc.patient
},
callback: function (data) {
var age = null;
if(data.message.dob){
age = calculate_age(data.message.dob);
}
frappe.model.set_value(frm.doctype,frm.docname, "patient_age", age);
show_details(data.message);
}
});

View File

@@ -40,6 +40,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -70,6 +71,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -100,6 +102,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -126,10 +129,11 @@
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -161,6 +165,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -192,6 +197,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -223,6 +229,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -254,6 +261,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -283,6 +291,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -313,6 +322,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -343,6 +353,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -373,6 +384,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -403,6 +415,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -433,6 +446,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -464,6 +478,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -493,6 +508,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -524,6 +540,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -554,6 +571,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -585,6 +603,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -615,6 +634,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -645,6 +665,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -674,6 +695,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -704,6 +726,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -734,6 +757,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -765,6 +789,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -796,6 +821,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
@@ -810,7 +836,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-10-05 16:08:24.624644",
"modified": "2018-03-09 07:05:24.984224",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Physician",

View File

@@ -81,7 +81,7 @@ frappe.ui.form.on('Employee',{
}
frappe.call({
method: "erpnext.hr.doctype.employee.employee.create_user",
args: { employee: cur_frm.doc.name },
args: { employee: frm.doc.name, email: frm.doc.prefered_email },
callback: function(r)
{
frm.set_value("user_id", r.message)

View File

@@ -263,7 +263,7 @@ def deactivate_sales_person(status = None, employee = None):
frappe.db.set_value("Sales Person", sales_person, "enabled", 0)
@frappe.whitelist()
def create_user(employee, user = None):
def create_user(employee, user = None, email=None):
emp = frappe.get_doc("Employee", employee)
employee_name = emp.employee_name.split(" ")
@@ -277,6 +277,9 @@ def create_user(employee, user = None):
first_name = employee_name[0]
if email:
emp.prefered_email = email
user = frappe.new_doc("User")
user.update({
"name": emp.employee_name,

View File

@@ -45,10 +45,11 @@ class ExpenseClaim(AccountsController):
}[cstr(self.docstatus or 0)]
paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_amount)
if self.total_sanctioned_amount > 0 and self.total_sanctioned_amount == paid_amount\
if (self.is_paid or (flt(self.total_sanctioned_amount) > 0
and flt(self.total_sanctioned_amount) == paid_amount)) \
and self.docstatus == 1 and self.approval_status == 'Approved':
self.status = "Paid"
elif self.total_sanctioned_amount > 0 and self.docstatus == 1 and self.approval_status == 'Approved':
self.status = "Paid"
elif flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1 and self.approval_status == 'Approved':
self.status = "Unpaid"
elif self.docstatus == 1 and self.approval_status == 'Rejected':
self.status = 'Rejected'

View File

@@ -5,7 +5,10 @@ var in_progress = false;
frappe.ui.form.on('Payroll Entry', {
onload: function (frm) {
frm.doc.posting_date = frappe.datetime.nowdate();
if (!frm.doc.posting_date) {
frm.doc.posting_date = frappe.datetime.nowdate();
}
frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet);
},

View File

@@ -175,7 +175,7 @@ class PayrollEntry(Document):
Get loan details from submitted salary slip based on selected criteria
"""
cond = self.get_filter_condition()
return frappe.db.sql(""" select eld.employee_loan_account,
return frappe.db.sql(""" select eld.employee_loan_account, eld.employee_loan,
eld.interest_income_account, eld.principal_amount, eld.interest_amount, eld.total_payment
from
`tabSalary Slip` t1, `tabSalary Slip Loan` eld
@@ -283,7 +283,12 @@ class PayrollEntry(Document):
"account": data.employee_loan_account,
"credit_in_account_currency": data.principal_amount
})
accounts.append({
if data.interest_amount and not data.interest_income_account:
frappe.throw(_("Select interest income account in employee loan {0}").format(data.employee_loan))
if data.interest_income_account and data.interest_amount:
accounts.append({
"account": data.interest_income_account,
"credit_in_account_currency": data.interest_amount,
"cost_center": self.cost_center,

View File

@@ -156,9 +156,10 @@ class SalarySlip(TransactionBase):
})
def get_date_details(self):
date_details = get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date)
self.start_date = date_details.start_date
self.end_date = date_details.end_date
if not self.end_date:
date_details = get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date)
self.start_date = date_details.start_date
self.end_date = date_details.end_date
def check_sal_struct(self, joining_date, relieving_date):
cond = ''

View File

@@ -47,7 +47,6 @@ class BOM(WebsiteGenerator):
self.validate_currency()
self.set_conversion_rate()
self.validate_uom_is_interger()
self.update_stock_qty()
self.set_bom_material_details()
self.validate_materials()
self.validate_operations()
@@ -247,14 +246,12 @@ class BOM(WebsiteGenerator):
set_default(self, "item")
item = frappe.get_doc("Item", self.item)
if item.default_bom != self.name:
item.default_bom = self.name
item.save(ignore_permissions = True)
frappe.db.set_value('Item', self.item, 'default_bom', self.name)
else:
frappe.db.set(self, "is_default", 0)
item = frappe.get_doc("Item", self.item)
if item.default_bom == self.name:
item.default_bom = None
item.save(ignore_permissions = True)
frappe.db.set_value('Item', self.item, 'default_bom', None)
def clear_operations(self):
if not self.with_operations:
@@ -291,6 +288,8 @@ class BOM(WebsiteGenerator):
m.uom = m.stock_uom
m.qty = m.stock_qty
m.db_update()
def validate_uom_is_interger(self):
from erpnext.utilities.transaction_base import validate_uom_is_integer
validate_uom_is_integer(self, "uom", "qty", "BOM Item")
@@ -333,19 +332,23 @@ class BOM(WebsiteGenerator):
def check_recursion(self):
""" Check whether recursion occurs in any bom"""
bom_list = self.traverse_tree()
bom_nos = frappe.get_all('BOM Item', fields=["bom_no"],
filters={'parent': ('in', bom_list), 'parenttype': 'BOM'})
check_list = [['parent', 'bom_no', 'parent'], ['bom_no', 'parent', 'child']]
for d in check_list:
bom_list, count = [self.name], 0
while (len(bom_list) > count ):
boms = frappe.db.sql(" select %s from `tabBOM Item` where %s = %s and parenttype='BOM'" %
(d[0], d[1], '%s'), cstr(bom_list[count]))
count = count + 1
for b in boms:
if b[0] == self.name:
frappe.throw(_("BOM recursion: {0} cannot be parent or child of {2}").format(b[0], self.name))
if b[0]:
bom_list.append(b[0])
raise_exception = False
if bom_nos and self.name in [d.bom_no for d in bom_nos]:
raise_exception = True
if not raise_exception:
bom_nos = frappe.get_all('BOM Item', fields=["parent"],
filters={'bom_no': self.name, 'parenttype': 'BOM'})
if self.name in [d.parent for d in bom_nos]:
raise_exception = True
if raise_exception:
frappe.throw(_("BOM recursion: {0} cannot be parent or child of {2}").format(self.name, self.name))
def update_cost_and_exploded_items(self, bom_list=[]):
bom_list = self.traverse_tree(bom_list)
@@ -594,9 +597,16 @@ def validate_bom_no(item, bom_no):
if bom.docstatus != 1:
if not getattr(frappe.flags, "in_test", False):
frappe.throw(_("BOM {0} must be submitted").format(bom_no))
if item and not (bom.item.lower() == item.lower() or \
bom.item.lower() == cstr(frappe.db.get_value("Item", item, "variant_of")).lower()):
frappe.throw(_("BOM {0} does not belong to Item {1}").format(bom_no, item))
if item:
rm_item_exists = False
for d in bom.items:
if (d.item_code.lower() == item.lower()):
rm_item_exists = True
if bom.item.lower() == item.lower() or \
bom.item.lower() == cstr(frappe.db.get_value("Item", item, "variant_of")).lower():
rm_item_exists = True
if not rm_item_exists:
frappe.throw(_("BOM {0} does not belong to Item {1}").format(bom_no, item))
@frappe.whitelist()
def get_children(doctype, parent=None, is_root=False, **filters):

View File

@@ -60,5 +60,14 @@ frappe.treeview_settings["BOM"] = {
condition: 'frappe.boot.user.can_create.indexOf("BOM") !== -1'
}
],
onrender: function(node) {
if(node.is_root && node.data.value!="BOM") {
frappe.model.with_doc("BOM", node.data.value, function() {
var bom = frappe.model.get_doc("BOM", node.data.value);
node.data.image = bom.image || "";
node.data.description = bom.description || "";
});
}
},
view_template: 'bom_item_preview'
}

View File

@@ -490,3 +490,8 @@ erpnext.patches.v10_0.set_default_payment_terms_based_on_company
erpnext.patches.v10_0.update_sales_order_link_to_purchase_order
erpnext.patches.v10_0.added_extra_gst_custom_field_in_gstr2 #2018-02-13
erpnext.patches.v10_0.set_b2c_limit
erpnext.patches.v10_0.update_status_for_multiple_source_in_po
erpnext.patches.v10_0.set_auto_created_serial_no_in_stock_entry
erpnext.patches.v10_0.update_territory_and_customer_group
erpnext.patches.v10_0.update_warehouse_address_details
erpnext.patches.v10_0.update_reserved_qty_for_purchase_order

View File

@@ -6,6 +6,10 @@ def execute():
if not company:
return
for doctype in ["Sales Invoice", "Delivery Note", "Purchase Invoice"]:
frappe.db.sql("""delete from `tabCustom Field` where dt = %s
and fieldname in ('port_code', 'shipping_bill_number', 'shipping_bill_date')""", doctype)
make_custom_fields()
frappe.db.sql("""
@@ -14,6 +18,4 @@ def execute():
where fieldname = 'reason_for_issuing_document'
""")
for doctype in ["Sales Invoice", "Delivery Note", "Purchase Invoice"]:
frappe.db.sql("""delete from `tabCustom Field` where dt = %s
and fieldname in ('port_code', 'shipping_bill_number', 'shipping_bill_date')""", doctype)

View File

@@ -0,0 +1,56 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
serialised_items = [d.name for d in frappe.get_all("Item", filters={"has_serial_no": 1})]
if not serialised_items:
return
for dt in ["Stock Entry Detail", "Purchase Receipt Item", "Purchase Invoice Item"]:
cond = ""
if dt=="Purchase Invoice Item":
cond = """ and parent in (select name from `tabPurchase Invoice`
where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.parent and update_stock=1)"""
item_rows = frappe.db.sql("""
select name
from `tab{0}`
where conversion_factor != 1
and docstatus = 1
and ifnull(serial_no, '') = ''
and item_code in ({1})
{2}
""".format(dt, ', '.join(['%s']*len(serialised_items)), cond), tuple(serialised_items))
if item_rows:
sle_serial_nos = dict(frappe.db.sql("""
select voucher_detail_no, serial_no
from `tabStock Ledger Entry`
where ifnull(serial_no, '') != ''
and voucher_detail_no in (%s)
""".format(', '.join(['%s']*len(item_rows))),
tuple([d[0] for d in item_rows])))
batch_size = 100
for i in range(0, len(item_rows), batch_size):
batch_item_rows = item_rows[i:i + batch_size]
when_then = []
for item_row in batch_item_rows:
when_then.append('WHEN `name` = "{row_name}" THEN "{value}"'.format(
row_name=item_row[0],
value=sle_serial_nos.get(item_row[0])))
frappe.db.sql("""
update
`tab{doctype}`
set
serial_no = CASE {when_then_cond} ELSE `serial_no` END
""".format(
doctype = dt,
when_then_cond=" ".join(when_then)
))

View File

@@ -0,0 +1,49 @@
import frappe
from erpnext.stock.utils import get_bin
def execute():
po_item = list(frappe.db.sql(("""
select distinct po.name as poname, poitem.rm_item_code as rm_item_code, po.company
from `tabPurchase Order` po, `tabPurchase Order Item Supplied` poitem
where po.name = poitem.parent
and po.is_subcontracted = "Yes"
and po.docstatus = 1"""), as_dict=1))
if not po_item:
return
frappe.reload_doc("stock", "doctype", "bin")
frappe.reload_doc("buying", "doctype", "purchase_order_item_supplied")
company_warehouse = frappe._dict(frappe.db.sql("""select company, min(name) from `tabWarehouse`
where is_group = 0 group by company"""))
items = list(set([d.rm_item_code for d in po_item]))
item_wh = frappe._dict(frappe.db.sql("""select item_code, default_warehouse
from `tabItem` where name in ({0})""".format(", ".join(["%s"] * len(items))), items))
# Update reserved warehouse
for item in po_item:
reserve_warehouse = get_warehouse(item.rm_item_code, item.company, company_warehouse, item_wh)
frappe.db.sql("""update `tabPurchase Order Item Supplied`
set reserve_warehouse = %s
where parent = %s and rm_item_code = %s
""", (reserve_warehouse, item["poname"], item["rm_item_code"]))
# Update bin
item_wh_bin = frappe.db.sql(("""
select distinct poitemsup.rm_item_code as rm_item_code,
poitemsup.reserve_warehouse as reserve_warehouse
from `tabPurchase Order` po, `tabPurchase Order Item Supplied` poitemsup
where po.name = poitemsup.parent
and po.is_subcontracted = "Yes"
and po.docstatus = 1"""), as_dict=1)
for d in item_wh_bin:
stock_bin = get_bin(d["rm_item_code"], d["reserve_warehouse"])
stock_bin.update_reserved_qty_for_sub_contracting()
def get_warehouse(item_code, company, company_warehouse, item_wh):
reserve_warehouse = item_wh.get(item_code)
if frappe.db.get_value("Warehouse", reserve_warehouse, "company") != company:
reserve_warehouse = None
if not reserve_warehouse:
reserve_warehouse = company_warehouse.get(company)
return reserve_warehouse

View File

@@ -0,0 +1,40 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
# update the sales order item in the material request
frappe.reload_doc('stock', 'doctype', 'material_request_item')
frappe.db.sql('''update `tabMaterial Request Item` mri, `tabSales Order Item` soi
set mri.sales_order_item = soi.name
where ifnull(mri.sales_order, "")!="" and soi.parent=mri.sales_order
and soi.item_code=mri.item_code and mri.docstatus=1
''')
# update the sales order item in the purchase order
frappe.db.sql('''update `tabPurchase Order Item` poi, `tabSales Order Item` soi
set poi.sales_order_item = soi.name
where ifnull(poi.sales_order, "")!="" and soi.parent=poi.sales_order
and soi.item_code=poi.item_code and poi.docstatus = 1
''')
# Update the status in material request and sales order
po_list = frappe.db.sql('''
select parent from `tabPurchase Order Item` where ifnull(material_request, "")!="" and
ifnull(sales_order, "")!="" and docstatus=1
''',as_dict=1)
for po in list(set([d.get("parent") for d in po_list if d.get("parent")])):
try:
po_doc = frappe.get_doc("Purchase Order", po)
# update the so in the status updater
po_doc.update_status_updater()
po_doc.update_qty(update_modified=False)
except Exception:
pass

View File

@@ -0,0 +1,29 @@
import frappe
from frappe.model.rename_doc import get_fetch_fields
def execute():
ignore_doctypes = ["Lead", "Opportunity", "POS Profile", "Tax Rule", "Pricing Rule"]
customers = frappe.get_all('Customer', fields=["name", "customer_group"])
customer_group_fetch = get_fetch_fields('Customer', 'Customer Group', ignore_doctypes)
batch_size = 1000
for i in range(0, len(customers), batch_size):
batch_customers = customers[i:i + batch_size]
for d in customer_group_fetch:
when_then = []
for customer in batch_customers:
value = frappe.db.escape(frappe.as_unicode(customer.get("customer_group")))
when_then.append('''
WHEN `%s` = "%s" and %s != "%s"
THEN "%s"
'''%(d["master_fieldname"], frappe.db.escape(frappe.as_unicode(customer.name)),
d["linked_to_fieldname"], value, value))
frappe.db.sql("""
update
`tab%s`
set
%s = CASE %s ELSE `%s` END
"""%(d['doctype'], d.linked_to_fieldname, " ".join(when_then), d.linked_to_fieldname))

View File

@@ -0,0 +1,37 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
warehouse = frappe.db.sql("""select name, email_id, phone_no, mobile_no, address_line_1,
address_line_2, city, state, pin from `tabWarehouse` where ifnull(address_line_1, '') != ''
or ifnull(mobile_no, '') != ''
or ifnull(email_id, '') != '' """, as_dict=1)
for d in warehouse:
try:
address = frappe.new_doc('Address')
address.name = d.name
address.address_title = d.name
address.address_line1 = d.address_line_1
address.city = d.city
address.state = d.state
address.pincode = d.pin
address.db_insert()
address.append('links',{'link_doctype':'Warehouse','link_name':d.name})
address.links[0].db_insert()
if d.name and (d.email_id or d.mobile_no or d.phone_no):
contact = frappe.new_doc('Contact')
contact.name = d.name
contact.first_name = d.name
contact.mobile_no = d.mobile_no
contact.email_id = d.email_id
contact.phone = d.phone_no
contact.db_insert()
contact.append('links',{'link_doctype':'Warehouse','link_name':d.name})
contact.links[0].db_insert()
except frappe.DuplicateEntryError:
pass

View File

@@ -10,6 +10,7 @@ def execute():
frappe.reload_doctype("Purchase Receipt")
frappe.reload_doctype("Sales Order Item")
frappe.reload_doctype("Purchase Order Item")
frappe.reload_doctype("Purchase Order Item Supplied")
# sales return
return_entries = list(frappe.db.sql("""
@@ -86,6 +87,6 @@ def execute():
""", (order_details[0].purchase_order, order_details[0].po_detail, d.row_id))
pr = frappe.get_doc("Purchase Receipt", d.name)
pr.update_ordered_qty()
pr.update_ordered_and_reserved_qty()
pr.update_prevdoc_status()

View File

@@ -290,7 +290,7 @@ def get_projectwise_timesheet_data(project, parent=None):
cond = "and parent = %(parent)s"
return frappe.db.sql("""select name, parent, billing_hours, billing_amount as billing_amt
from `tabTimesheet Detail` where docstatus=1 and project = %(project)s {0} and billable = 1
from `tabTimesheet Detail` where parenttype = 'Timesheet' and docstatus=1 and project = %(project)s {0} and billable = 1
and sales_invoice is null""".format(cond), {'project': project, 'parent': parent}, as_dict=1)
@frappe.whitelist()

View File

@@ -37,20 +37,22 @@
<div class="cell price-cell text-right tax-table">
</div>
</div>
<div class="pos-list-row discount-amount-area">
<div class="cell"></div>
<div class="cell text-right">{%= __("Discount") %}</div>
<div class="cell price-cell discount-field-col">
<div class="input-group input-group-sm">
<span class="input-group-addon">%</span>
<input type="text" class="form-control discount-percentage text-right">
</div>
<div class="input-group input-group-sm">
<span class="input-group-addon">{%= get_currency_symbol(currency) %}</span>
<input type="text" class="form-control discount-amount text-right" placeholder="{%= 0.00 %}">
{% if(allow_user_to_edit_discount) { %}
<div class="pos-list-row discount-amount-area">
<div class="cell"></div>
<div class="cell text-right">{%= __("Discount") %}</div>
<div class="cell price-cell discount-field-col">
<div class="input-group input-group-sm">
<span class="input-group-addon">%</span>
<input type="text" class="form-control discount-percentage text-right">
</div>
<div class="input-group input-group-sm">
<span class="input-group-addon">{%= get_currency_symbol(currency) %}</span>
<input type="text" class="form-control discount-amount text-right" placeholder="{%= 0.00 %}">
</div>
</div>
</div>
</div>
{% } %}
<div class="pos-list-row grand-total-area collapse-btn" style="border-bottom:1px solid #d1d8dd;">
<div class="cell">
<a class="">
@@ -71,7 +73,7 @@
{% for(var j=i*3; j
<(i+1)*3; j++) { %} <button type="button" class="btn btn-default numeric-keypad" val="{{j+1}}">{{j+1}}</button>
{% } %}
<button type="button" {% if(!allow_user_to_edit_rate && chartData[i] == __("Price")) { %} disabled {% } %} id="pos-item-{{ chartData[i].toLowerCase() }}" class="btn text-center btn-default numeric-keypad pos-operation">{{ __(chartData[i]) }}</button>
<button type="button" {% if((!allow_user_to_edit_rate && chartData[i] == __("Price")) || (!allow_user_to_edit_discount && chartData[i] == __("Disc"))) { %} disabled {% } %} id="pos-item-{{ chartData[i].toLowerCase() }}" class="btn text-center btn-default numeric-keypad pos-operation">{{ __(chartData[i]) }}</button>
</div>
{% } %}
<div class="row text-right">

View File

@@ -9,7 +9,7 @@
</div>
<div class="pos-list-row">
<div class="cell">{{ __("Discount") }}: %</div>
<input type="tel" class="form-control cell pos-item-disc" value="{%= discount_percentage %}">
<input type="tel" class="form-control cell pos-item-disc" {% if !allow_user_to_edit_discount %} disabled {% endif %} value="{%= discount_percentage %}">
</div>
<div class="pos-list-row">
<div class="cell">{{ __("Price") }}:</div>

View File

@@ -64,7 +64,7 @@ $.extend(shopping_cart, {
},
set_cart_count: function() {
var cart_count = getCookie("cart_count");
var cart_count = frappe.get_cookie("cart_count");
if(frappe.session.user==="Guest") {
cart_count = 0;
}

View File

@@ -11,6 +11,7 @@ from frappe.desk.reportview import build_match_conditions
from erpnext.utilities.transaction_base import TransactionBase
from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this
from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
from frappe.model.rename_doc import update_linked_doctypes
class Customer(TransactionBase):
def get_feed(self):
@@ -53,6 +54,14 @@ class Customer(TransactionBase):
self.flags.old_lead = self.lead_name
validate_party_accounts(self)
self.validate_credit_limit_on_change()
self.check_customer_group_change()
def check_customer_group_change(self):
frappe.flags.customer_group_changed = False
if not self.get('__islocal'):
if self.customer_group != frappe.db.get_value('Customer', self.name, 'customer_group'):
frappe.flags.customer_group_changed = True
def on_update(self):
self.validate_name_with_customer_group()
@@ -65,6 +74,14 @@ class Customer(TransactionBase):
if self.flags.is_new_doc:
self.create_lead_address_contact()
self.update_customer_groups()
def update_customer_groups(self):
ignore_doctypes = ["Lead", "Opportunity", "POS Profile", "Tax Rule", "Pricing Rule"]
if frappe.flags.customer_group_changed:
update_linked_doctypes('Customer', self.name, 'Customer Group',
self.customer_group, ignore_doctypes)
def create_primary_contact(self):
if not self.customer_primary_contact and not self.lead_name:
if self.mobile_no or self.email_id:

View File

@@ -41,7 +41,7 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
var me = this;
if (doc.__islocal) {
if (doc.__islocal && !doc.valid_till) {
this.frm.set_value('valid_till', frappe.datetime.add_months(doc.transaction_date, 1))
}

View File

@@ -48,6 +48,15 @@ frappe.ui.form.on("Sales Order", {
});
frappe.ui.form.on("Sales Order Item", {
item_code: function(frm,cdt,cdn) {
var row = locals[cdt][cdn];
if (frm.doc.delivery_date) {
row.delivery_date = frm.doc.delivery_date;
refresh_field("delivery_date", cdn, "items");
} else {
this.frm.script_manager.copy_from_first_row("items", row, ["delivery_date"]);
}
},
delivery_date: function(frm, cdt, cdn) {
if(!frm.doc.delivery_date) {
erpnext.utils.copy_value_in_all_row(frm.doc, cdt, cdn, "items", "delivery_date");
@@ -433,17 +442,6 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
if(cint(frappe.boot.notification_settings.sales_order)) {
this.frm.email_doc(frappe.boot.notification_settings.sales_order_message);
}
},
items_add: function(doc, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn);
if(doc.delivery_date) {
row.delivery_date = doc.delivery_date;
refresh_field("delivery_date", cdn, "items");
} else {
this.frm.script_manager.copy_from_first_row("items", row, ["delivery_date"]);
}
}
});
$.extend(cur_frm.cscript, new erpnext.selling.SalesOrderController({frm: cur_frm}));

View File

@@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe
import json
import frappe.utils
from frappe.utils import cstr, flt, getdate, comma_and, cint, nowdate, add_days
from frappe.utils import cstr, flt, getdate, comma_and, cint
from frappe import _
from frappe.model.utils import get_fetch_values
from frappe.model.mapper import get_mapped_doc
@@ -104,8 +104,8 @@ class SalesOrder(SellingController):
def validate_delivery_date(self):
if self.order_type == 'Sales':
if not self.delivery_date:
self.delivery_date = max([d.delivery_date for d in self.get("items") if d.delivery_date])
delivery_date_list = [d.delivery_date for d in self.get("items") if d.delivery_date]
self.delivery_date = max(delivery_date_list) if delivery_date_list else None
if self.delivery_date:
for d in self.get("items"):
if not d.delivery_date:
@@ -435,6 +435,7 @@ def make_material_request(source_name, target_doc=None):
"Sales Order Item": {
"doctype": "Material Request Item",
"field_map": {
"name": "sales_order_item",
"parent": "sales_order",
"stock_uom": "uom",
"stock_qty": "qty"
@@ -476,13 +477,9 @@ def make_project(source_name, target_doc=None):
@frappe.whitelist()
def make_delivery_note(source_name, target_doc=None):
def set_missing_values(source, target):
so = [d.against_sales_order for d in target.items]
if so:
po_no_list = frappe.get_all('Sales Order', 'po_no', filters = {'name': ('in', so)})
target.po_no = ', '.join(d.po_no for d in po_no_list if d.po_no)
target.ignore_pricing_rule = 1
target.run_method("set_missing_values")
target.run_method("set_po_nos")
target.run_method("calculate_taxes_and_totals")
# set company address
@@ -543,6 +540,7 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
target.ignore_pricing_rule = 1
target.flags.ignore_permissions = True
target.run_method("set_missing_values")
target.run_method("set_po_nos")
target.run_method("calculate_taxes_and_totals")
# set company address

View File

@@ -257,12 +257,16 @@ erpnext.pos.PointOfSale = class PointOfSale {
frappe.msgprint(__("Quantity must be positive"));
value = item.qty;
} else {
item[field] = value;
if (field == "serial_no" && value) {
let serial_nos = value.split("\n");
item["qty"] = serial_nos.filter(d => {
return d!=="";
}).length;
if (in_list(["qty", "serial_no", "batch"], field)) {
item[field] = value;
if (field == "serial_no" && value) {
let serial_nos = value.split("\n");
item["qty"] = serial_nos.filter(d => {
return d!=="";
}).length;
}
} else {
return frappe.model.set_value(item.doctype, item.name, field, value);
}
}
@@ -1475,7 +1479,7 @@ class Payment {
fieldname: p.mode_of_payment,
default: p.amount,
onchange: () => {
const value = this.dialog.get_value(this.fieldname);
const value = this.dialog.get_value(this.fieldname) || 0;
me.update_payment_value(this.fieldname, value);
}
};

View File

@@ -102,7 +102,7 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non
or I.name like %(search)s)"""
search = "%" + cstr(search) + "%"
query += """order by I.weightage desc, in_stock desc, I.item_name limit %s, %s""" % (start, limit)
query += """order by I.weightage desc, in_stock desc, I.modified desc limit %s, %s""" % (start, limit)
data = frappe.db.sql(query, {"product_group": product_group,"search": search, "today": nowdate()}, as_dict=1)

View File

@@ -90,7 +90,7 @@ erpnext.stock.ItemDashboard = Class.extend({
data.forEach(function(d) {
d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production;
d.pending_qty = 0;
d.total_reserved = d.reserved_qty + d.reserved_qty_for_production;
d.total_reserved = d.reserved_qty + d.reserved_qty_for_production + d.reserved_qty_for_sub_contract;
if(d.actual_or_pending > d.actual_qty) {
d.pending_qty = d.actual_or_pending - d.actual_qty;
}

View File

@@ -26,13 +26,14 @@ def get_data(item_code=None, warehouse=None, item_group=None,
return frappe.db.sql('''
select
b.item_code, b.warehouse, b.projected_qty, b.reserved_qty,
b.reserved_qty_for_production, b.actual_qty, b.valuation_rate, i.item_name
b.reserved_qty_for_production, b.reserved_qty_for_sub_contract, b.actual_qty, b.valuation_rate, i.item_name
from
tabBin b, tabItem i
where
b.item_code = i.name
and
(b.projected_qty != 0 or b.reserved_qty != 0 or b.reserved_qty_for_production != 0 or b.actual_qty != 0)
(b.projected_qty != 0 or b.reserved_qty != 0 or b.reserved_qty_for_production != 0
or b.reserved_qty_for_sub_contract != 0 or b.actual_qty != 0)
{conditions}
order by
{sort_by} {sort_order}

View File

@@ -3,7 +3,7 @@
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "",
"autoname": "field:batch_id",
"beta": 0,
"creation": "2013-03-05 14:50:38",
"custom": 0,
@@ -27,7 +27,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Batch ID",
"length": 0,
@@ -58,7 +58,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Item",
"length": 0,
@@ -425,7 +425,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 5,
"modified": "2017-05-21 21:00:11.096038",
"modified": "2018-03-12 16:37:20.144750",
"modified_by": "Administrator",
"module": "Stock",
"name": "Batch",

View File

@@ -296,6 +296,36 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "reserved_qty_for_sub_contract",
"fieldtype": "Float",
"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": "Reserved Qty for sub contract",
"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,
@@ -463,7 +493,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-06-13 13:06:32.601505",
"modified": "2017-11-22 08:14:30.615638",
"modified_by": "Administrator",
"module": "Stock",
"name": "Bin",

View File

@@ -61,7 +61,7 @@ class Bin(Document):
def set_projected_qty(self):
self.projected_qty = (flt(self.actual_qty) + flt(self.ordered_qty)
+ flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty)
- flt(self.reserved_qty_for_production))
- flt(self.reserved_qty_for_production) - flt(self.reserved_qty_for_sub_contract))
def get_first_sle(self):
sle = frappe.db.sql("""
@@ -90,6 +90,42 @@ class Bin(Document):
self.db_set('reserved_qty_for_production', flt(self.reserved_qty_for_production))
self.db_set('projected_qty', self.projected_qty)
def update_reserved_qty_for_sub_contracting(self):
#reserved qty
reserved_qty_for_sub_contract = frappe.db.sql('''
select ifnull(sum(itemsup.required_qty),0)
from `tabPurchase Order` po, `tabPurchase Order Item Supplied` itemsup
where
itemsup.rm_item_code = %s
and itemsup.parent = po.name
and po.docstatus = 1
and po.is_subcontracted = 'Yes'
and po.status != 'Closed'
and po.per_received < 100
and itemsup.reserve_warehouse = %s''', (self.item_code, self.warehouse))[0][0]
#Get Transferred Entries
materials_transferred = frappe.db.sql("""
select
ifnull(sum(transfer_qty),0)
from
`tabStock Entry` se, `tabStock Entry Detail` sed, `tabPurchase Order` po
where
se.docstatus=1
and se.purpose='Subcontract'
and ifnull(se.purchase_order, '') !=''
and sed.item_code = %s
and se.name = sed.parent
and se.purchase_order = po.name
and po.docstatus = 1
and po.is_subcontracted = 'Yes'
and po.status != 'Closed'
and po.per_received < 100
""", (self.item_code))[0][0]
self.db_set('reserved_qty_for_sub_contract', (reserved_qty_for_sub_contract - materials_transferred))
self.set_projected_qty()
self.db_set('projected_qty', self.projected_qty)
def on_doctype_update():
frappe.db.add_index("Bin", ["item_code", "warehouse"])

View File

@@ -493,7 +493,7 @@
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
@@ -508,14 +508,14 @@
"in_standard_filter": 0,
"label": "Customer's Purchase Order No",
"length": 0,
"no_copy": 0,
"no_copy": 1,
"oldfieldname": "po_no",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"print_width": "100px",
"read_only": 1,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
@@ -3701,7 +3701,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2018-01-12 15:27:44.471335",
"modified": "2018-03-13 15:35:02.234116",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",

View File

@@ -387,6 +387,7 @@ def make_sales_invoice(source_name, target_doc=None):
target.is_pos = 0
target.ignore_pricing_rule = 1
target.run_method("set_missing_values")
target.run_method("set_po_nos")
if len(target.get("items")) == 0:
frappe.throw(_("All these items have already been invoiced"))

View File

@@ -426,7 +426,9 @@ $.extend(erpnext.item, {
filters: [
["parent","=", attribute]
],
fields: ["attribute_value"]
fields: ["attribute_value"],
limit_start: 0,
limit_page_length: 500
}
}).then((r) => {
if(r.message) {

View File

@@ -80,7 +80,6 @@ class TestItem(unittest.TestCase):
}
make_test_objects("Item Price")
print(frappe.get_all("Item Price"))
details = get_item_details({
"item_code": "_Test Item",

View File

@@ -72,6 +72,38 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"description": "Rename Attribute Value in Item Attribute.",
"fieldname": "allow_rename_attribute_value",
"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": "Allow Rename Attribute Value",
"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,
@@ -144,7 +176,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2017-11-14 15:54:12.190518",
"modified": "2018-02-19 11:39:54.401128",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item Variant Settings",

View File

@@ -241,7 +241,8 @@ def make_purchase_order(source_name, target_doc=None):
["parent", "material_request"],
["uom", "stock_uom"],
["uom", "uom"],
["sales_order", "sales_order"]
["sales_order", "sales_order"],
["sales_order_item", "sales_order_item"]
],
"postprocess": update_item,
"condition": lambda doc: doc.ordered_qty < doc.stock_qty

View File

@@ -670,6 +670,36 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sales_order_item",
"fieldtype": "Data",
"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": "Sales Order Item",
"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": 1,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -898,8 +928,8 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-12-15 16:29:18.902085",
"modified_by": "nabinhait@gmail.com",
"modified": "2018-02-12 05:51:39.954530",
"modified_by": "Administrator",
"module": "Stock",
"name": "Material Request Item",
"owner": "Administrator",

View File

@@ -122,7 +122,8 @@ class PurchaseReceipt(BuyingController):
self.update_billing_status()
# Updating stock ledger should always be called after updating prevdoc status,
# because updating ordered qty in bin depends upon updated ordered qty in PO
# because updating ordered qty, reserved_qty_for_subcontract in bin
# depends upon updated ordered qty in PO
self.update_stock_ledger()
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit

View File

@@ -11,7 +11,7 @@ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchas
from erpnext import set_perpetual_inventory
from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError
from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.stock.doctype.item.test_item import make_item
class TestPurchaseReceipt(unittest.TestCase):
def setUp(self):
@@ -203,6 +203,22 @@ class TestPurchaseReceipt(unittest.TestCase):
"delivery_document_no": return_pr.name
})
def test_purchase_return_for_multi_uom(self):
item_code = "_Test Purchase Return For Multi-UOM"
if not frappe.db.exists('Item', item_code):
item = make_item(item_code, {'stock_uom': 'Box'})
row = item.append('uoms', {
'uom': 'Unit',
'conversion_factor': 0.1
})
row.db_update()
pr = make_purchase_receipt(item_code=item_code, qty=1, uom="Box", conversion_factor=1.0)
return_pr = make_purchase_receipt(item_code=item_code, qty=-10, uom="Unit",
stock_uom="Box", conversion_factor=0.1, is_return=1, return_against=pr.name)
self.assertEquals(abs(return_pr.items[0].stock_qty), 1.0)
def test_closed_purchase_receipt(self):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_purchase_receipt_status
@@ -255,7 +271,6 @@ class TestPurchaseReceipt(unittest.TestCase):
def test_not_accept_duplicate_serial_no(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
item_code = frappe.db.get_value('Item', {'has_serial_no': 1})
@@ -307,9 +322,10 @@ def make_purchase_receipt(**args):
"rejected_qty": rejected_qty,
"rejected_warehouse": args.rejected_warehouse or "_Test Rejected Warehouse - _TC" if rejected_qty != 0 else "",
"rate": args.rate or 50,
"conversion_factor": 1.0,
"conversion_factor": args.conversion_factor or 1.0,
"serial_no": args.serial_no,
"stock_uom": "_Test UOM"
"stock_uom": args.stock_uom or "_Test UOM",
"uom": args.uom or "_Test UOM"
})
if not args.do_not_save:

View File

@@ -191,7 +191,7 @@ def validate_serial_no(sle, item_det):
if sle.serial_no:
frappe.throw(_("Item {0} is not setup for Serial Nos. Column must be blank").format(sle.item_code),
SerialNoNotRequiredError)
else:
elif sle.is_cancelled == "No":
if sle.serial_no:
serial_nos = get_serial_nos(sle.serial_no)
if cint(sle.actual_qty) != flt(sle.actual_qty):
@@ -326,11 +326,16 @@ def update_serial_nos_after_submit(controller, parentfield):
update_rejected_serial_nos = True if (controller.doctype in ("Purchase Receipt", "Purchase Invoice")
and d.rejected_qty) else False
accepted_serial_nos_updated = False
warehouse = d.t_warehouse if controller.doctype == "Stock Entry" else d.warehouse
if controller.doctype == "Stock Entry":
warehouse = d.t_warehouse
qty = d.transfer_qty
else:
warehouse = d.warehouse
qty = d.stock_qty
for sle in stock_ledger_entries:
if sle.voucher_detail_no==d.name:
if not accepted_serial_nos_updated and d.qty and abs(sle.actual_qty)==d.qty \
if not accepted_serial_nos_updated and qty and abs(sle.actual_qty)==qty \
and sle.warehouse == warehouse and sle.serial_no != d.serial_no:
d.serial_no = sle.serial_no
frappe.db.set_value(d.doctype, d.name, "serial_no", sle.serial_no)

View File

@@ -278,6 +278,14 @@ frappe.ui.form.on('Stock Entry', {
frm.set_value("total_additional_costs",
flt(total_additional_costs, precision("total_additional_costs")));
},
source_warehouse_address: function(frm) {
erpnext.utils.get_address_display(frm, 'source_warehouse_address', 'source_address_display', false);
},
target_warehouse_address: function(frm) {
erpnext.utils.get_address_display(frm, 'target_warehouse_address', 'target_address_display', false);
},
})
frappe.ui.form.on('Stock Entry Detail', {

View File

@@ -737,6 +737,68 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "from_warehouse",
"fieldname": "source_warehouse_address",
"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": "Source Warehouse Address",
"length": 0,
"no_copy": 0,
"options": "Address",
"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": "source_address_display",
"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": "Source Warehouse Address",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"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,
@@ -797,6 +859,68 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "to_warehouse",
"fieldname": "target_warehouse_address",
"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": "Target Warehouse Name",
"length": 0,
"no_copy": 0,
"options": "Address",
"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": "target_address_display",
"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": "Target Warehouse Address",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"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,
@@ -1739,7 +1863,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-06-13 14:28:47.818067",
"modified": "2018-03-01 12:27:12.884611",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry",

View File

@@ -11,6 +11,7 @@ from erpnext.stock.stock_ledger import get_previous_sle, NegativeStockError, get
from erpnext.stock.get_item_details import get_bin_details, get_default_cost_center, get_conversion_factor
from erpnext.stock.doctype.batch.batch import get_batch_no, set_batch_nos, get_batch_qty
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
from erpnext.stock.utils import get_bin
import json
class IncorrectValuationRateError(frappe.ValidationError): pass
@@ -63,17 +64,22 @@ class StockEntry(StockController):
self.calculate_rate_and_amount(update_finished_item_rate=False)
def on_submit(self):
self.update_stock_ledger()
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
update_serial_nos_after_submit(self, "items")
self.update_production_order()
self.validate_purchase_order()
if self.purchase_order and self.purpose == "Subcontract":
self.update_purchase_order_supplied_items()
self.make_gl_entries()
def on_cancel(self):
self.update_stock_ledger()
self.update_production_order()
if self.purchase_order and self.purpose == "Subcontract":
self.update_purchase_order_supplied_items()
self.make_gl_entries_on_cancel()
def validate_purpose(self):
@@ -403,7 +409,7 @@ class StockEntry(StockController):
"""validation: finished good quantity should be same as manufacturing quantity"""
items_with_target_warehouse = []
for d in self.get('items'):
if d.bom_no and flt(d.transfer_qty) != flt(self.fg_completed_qty) and (d.t_warehouse != getattr(self, "pro_doc", frappe._dict()).scrap_warehouse):
if self.purpose != "Subcontract" and d.bom_no and flt(d.transfer_qty) != flt(self.fg_completed_qty) and (d.t_warehouse != getattr(self, "pro_doc", frappe._dict()).scrap_warehouse):
frappe.throw(_("Quantity in row {0} ({1}) must be same as manufactured quantity {2}"). \
format(d.idx, d.transfer_qty, self.fg_completed_qty))
@@ -582,19 +588,32 @@ class StockEntry(StockController):
frappe.throw(_("Manufacturing Quantity is mandatory"))
item_dict = self.get_bom_raw_materials(self.fg_completed_qty)
#Get PO Supplied Items Details
if self.purchase_order and self.purpose == "Subcontract":
#Get PO Supplied Items Details
item_wh = frappe._dict(frappe.db.sql("""
select rm_item_code, reserve_warehouse
from `tabPurchase Order` po, `tabPurchase Order Item Supplied` poitemsup
where po.name = poitemsup.parent
and po.name = %s""",self.purchase_order))
for item in item_dict.values():
if self.pro_doc and not self.pro_doc.skip_transfer:
item["from_warehouse"] = self.pro_doc.wip_warehouse
#Get Reserve Warehouse from PO
if self.purchase_order and self.purpose=="Subcontract":
item["from_warehouse"] = item_wh.get(item.item_code)
item["to_warehouse"] = self.to_warehouse if self.purpose=="Subcontract" else ""
self.add_to_stock_entry_detail(item_dict)
scrap_item_dict = self.get_bom_scrap_material(self.fg_completed_qty)
for item in scrap_item_dict.values():
if self.pro_doc and self.pro_doc.scrap_warehouse:
item["to_warehouse"] = self.pro_doc.scrap_warehouse
self.add_to_stock_entry_detail(scrap_item_dict, bom_no=self.bom_no)
if self.purpose != "Subcontract":
scrap_item_dict = self.get_bom_scrap_material(self.fg_completed_qty)
for item in scrap_item_dict.values():
if self.pro_doc and self.pro_doc.scrap_warehouse:
item["to_warehouse"] = self.pro_doc.scrap_warehouse
self.add_to_stock_entry_detail(scrap_item_dict, bom_no=self.bom_no)
# fetch the serial_no of the first stock entry for the second stock entry
if self.production_order and self.purpose == "Manufacture":
@@ -782,7 +801,7 @@ class StockEntry(StockController):
def add_to_stock_entry_detail(self, item_dict, bom_no=None):
expense_account, cost_center = frappe.db.get_values("Company", self.company, \
["default_expense_account", "cost_center"])[0]
for d in item_dict:
stock_uom = item_dict[d].get("stock_uom") or frappe.db.get_value("Item", d, "stock_uom")
@@ -833,7 +852,20 @@ class StockEntry(StockController):
if getdate(self.posting_date) > getdate(expiry_date):
frappe.throw(_("Batch {0} of Item {1} has expired.").format(item.batch_no, item.item_code))
def update_purchase_order_supplied_items(self):
#Get PO Supplied Items Details
item_wh = frappe._dict(frappe.db.sql("""
select rm_item_code, reserve_warehouse
from `tabPurchase Order` po, `tabPurchase Order Item Supplied` poitemsup
where po.name = poitemsup.parent
and po.name = %s""", self.purchase_order))
#Update reserved sub contracted quantity in bin based on Supplied Item Details
for d in self.get("items"):
reserve_warehouse = item_wh.get(d.item_code)
stock_bin = get_bin(d.item_code, reserve_warehouse)
stock_bin.update_reserved_qty_for_sub_contracting()
@frappe.whitelist()
def move_sample_to_retention_warehouse(company, items):
if isinstance(items, basestring):

View File

@@ -562,9 +562,8 @@ class TestStockEntry(unittest.TestCase):
rm_cost = 0
for d in stock_entry.get("items"):
if d.s_warehouse:
if d.item_code != "_Test FG Item 2":
rm_cost += flt(d.amount)
fg_cost = filter(lambda x: x.item_code=="_Test FG Item 2", stock_entry.get("items"))[0].amount
self.assertEqual(fg_cost,
flt(rm_cost + bom_operation_cost + production_order.additional_operating_cost, 2))

View File

@@ -5,6 +5,15 @@
frappe.ui.form.on("Warehouse", {
refresh: function(frm) {
frm.toggle_display('warehouse_name', frm.doc.__islocal);
frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
if(!frm.doc.__islocal) {
frappe.contacts.render_address_and_contact(frm);
} else {
frappe.contacts.clear_address_and_contact(frm);
}
frm.add_custom_button(__("Stock Balance"), function() {
frappe.set_route("query-report", "Stock Balance", {"warehouse": frm.doc.name});

View File

@@ -226,6 +226,125 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "address_and_contact",
"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": "Address and Contact",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "address_html",
"fieldtype": "HTML",
"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": "Address HTML",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_10",
"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,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "contact_html",
"fieldtype": "HTML",
"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": "Contact HTML",
"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,
@@ -699,7 +818,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-01-23 16:45:45.546649",
"modified": "2018-02-28 09:15:33.463838",
"modified_by": "Administrator",
"module": "Stock",
"name": "Warehouse",

View File

@@ -7,6 +7,7 @@ from frappe.utils import cint, validate_email_add
from frappe import throw, _
from frappe.utils.nestedset import NestedSet
from erpnext.stock import get_warehouse_account
from frappe.contacts.address_and_contact import load_address_and_contact
class Warehouse(NestedSet):
nsm_parent_field = 'parent_warehouse'
@@ -25,10 +26,8 @@ class Warehouse(NestedSet):
if account:
self.set_onload('account', account)
load_address_and_contact(self)
def validate(self):
if self.email_id:
validate_email_add(self.email_id, True)
def on_update(self):
self.update_nsm_model()

View File

@@ -48,6 +48,7 @@ frappe.pages['stock-balance'].on_page_load = function(wrapper) {
{fieldname: 'projected_qty', label: __('Projected qty')},
{fieldname: 'reserved_qty', label: __('Reserved for sale')},
{fieldname: 'reserved_qty_for_production', label: __('Reserved for manufacturing')},
{fieldname: 'reserved_qty_for_sub_contract', label: __('Reserved for sub contracting')},
{fieldname: 'actual_qty', label: __('Actual qty in stock')},
]
},

View File

@@ -24,9 +24,8 @@ def execute(filters=None):
data = []
for item in items:
total_outgoing = consumed_item_map.get(item.name, 0)+delivered_item_map.get(item.name,0)
avg_daily_outgoing = flt(total_outgoing/diff, float_preceision)
total_outgoing = consumed_item_map.get(item.name, 0) + delivered_item_map.get(item.name,0)
avg_daily_outgoing = flt(total_outgoing / diff, float_preceision)
reorder_level = (avg_daily_outgoing * flt(item.lead_time_days)) + flt(item.safety_stock)
data.append([item.name, item.item_name, item.description, item.safety_stock, item.lead_time_days,
@@ -45,15 +44,14 @@ def get_columns():
def get_item_info():
return frappe.db.sql("""select name, item_name, description, safety_stock,
lead_time_days from tabItem""", as_dict=1)
lead_time_days from tabItem""", as_dict=1)
def get_consumed_items(condition):
cn_items = frappe.db.sql("""select se_item.item_code,
sum(se_item.actual_qty) as 'consume_qty'
sum(se_item.transfer_qty) as 'consume_qty'
from `tabStock Entry` se, `tabStock Entry Detail` se_item
where se.name = se_item.parent and se.docstatus = 1
and ifnull(se_item.t_warehouse, '') = '' %s
and (ifnull(se_item.t_warehouse, '') = '' or se.purpose = 'Subcontract') %s
group by se_item.item_code""" % (condition), as_dict=1)
cn_items_map = {}
@@ -63,17 +61,16 @@ def get_consumed_items(condition):
return cn_items_map
def get_delivered_items(condition):
dn_items = frappe.db.sql("""select dn_item.item_code, sum(dn_item.qty) as dn_qty
dn_items = frappe.db.sql("""select dn_item.item_code, sum(dn_item.stock_qty) as dn_qty
from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item
where dn.name = dn_item.parent and dn.docstatus = 1 %s
group by dn_item.item_code""" % (condition), as_dict=1)
si_items = frappe.db.sql("""select si_item.item_name, sum(si_item.qty) as si_qty
si_items = frappe.db.sql("""select si_item.item_code, sum(si_item.stock_qty) as si_qty
from `tabSales Invoice` si, `tabSales Invoice Item` si_item
where si.name = si_item.parent and si.docstatus = 1 and
si.update_stock = 1 and si.is_pos = 1 %s
group by si_item.item_name""" % (condition), as_dict=1)
si.update_stock = 1 %s
group by si_item.item_code""" % (condition), as_dict=1)
dn_item_map = {}
for item in dn_items:

View File

@@ -96,6 +96,8 @@ def get_item_conditions(filters):
def get_sle_conditions(filters):
conditions = []
if filters.get("warehouse"):
conditions.append("warehouse=%(warehouse)s")
lft, rgt = frappe.db.get_value('Warehouse', filters.get("warehouse"), ['lft', 'rgt'])
conditions.append("""warehouse in (select wh.name from `tabWarehouse` wh
where wh.lft >= {0} and rgt <= {1})""".format(lft, rgt))
return "and {}".format(" and ".join(conditions)) if conditions else ""

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