2015-03-03 09:25:30 +00:00
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
2013-08-05 09:29:54 +00:00
# License: GNU General Public License v3. See license.txt
2013-03-19 06:31:24 +00:00
from __future__ import unicode_literals
2017-06-19 07:24:59 +00:00
import frappe , erpnext
2017-01-16 11:53:20 +00:00
from frappe . utils import cint , flt , cstr
2019-01-22 18:58:37 +00:00
from frappe import _
2014-02-14 10:17:51 +00:00
import frappe . defaults
2016-03-04 09:03:49 +00:00
from erpnext . accounts . utils import get_fiscal_year
2014-10-06 06:23:52 +00:00
from erpnext . accounts . general_ledger import make_gl_entries , delete_gl_entries , process_gl_map
2015-08-24 09:02:38 +00:00
from erpnext . controllers . accounts_controller import AccountsController
2017-01-25 13:17:53 +00:00
from erpnext . stock . stock_ledger import get_valuation_rate
2017-06-15 05:39:27 +00:00
from erpnext . stock import get_warehouse_account_map
2013-03-19 06:31:24 +00:00
2018-12-24 09:24:42 +00:00
class QualityInspectionRequiredError ( frappe . ValidationError ) : pass
class QualityInspectionRejectedError ( frappe . ValidationError ) : pass
2018-12-28 11:23:00 +00:00
class QualityInspectionNotSubmittedError ( frappe . ValidationError ) : pass
2018-12-24 09:24:42 +00:00
2013-03-19 06:31:24 +00:00
class StockController ( AccountsController ) :
2016-11-16 11:51:59 +00:00
def validate ( self ) :
super ( StockController , self ) . validate ( )
self . validate_inspection ( )
2019-12-30 07:56:47 +00:00
self . validate_serialized_batch ( )
2020-04-03 10:16:48 +00:00
self . validate_customer_provided_item ( )
2017-01-16 11:53:20 +00:00
2016-12-30 10:51:35 +00:00
def make_gl_entries ( self , gl_entries = None , repost_future_gle = True , from_repost = False ) :
2014-03-28 08:25:00 +00:00
if self . docstatus == 2 :
delete_gl_entries ( voucher_type = self . doctype , voucher_no = self . name )
2014-04-07 13:21:58 +00:00
2017-06-19 07:24:59 +00:00
if cint ( erpnext . is_perpetual_inventory_enabled ( self . company ) ) :
2019-03-08 05:43:35 +00:00
warehouse_account = get_warehouse_account_map ( self . company )
2014-04-07 13:21:58 +00:00
2014-03-28 08:25:00 +00:00
if self . docstatus == 1 :
2016-12-30 10:51:35 +00:00
if not gl_entries :
gl_entries = self . get_gl_entries ( warehouse_account )
make_gl_entries ( gl_entries , from_repost = from_repost )
2013-10-22 18:21:41 +00:00
2020-03-02 09:49:18 +00:00
if ( repost_future_gle or self . flags . repost_future_gle ) :
2014-09-26 08:52:18 +00:00
items , warehouses = self . get_items_and_warehouses ( )
2014-10-06 06:23:52 +00:00
update_gl_entries_after ( self . posting_date , self . posting_time , warehouses , items ,
2019-03-08 05:43:35 +00:00
warehouse_account , company = self . company )
2018-06-12 08:24:40 +00:00
elif self . doctype in [ ' Purchase Receipt ' , ' Purchase Invoice ' ] and self . docstatus == 1 :
2018-05-16 12:46:08 +00:00
gl_entries = [ ]
gl_entries = self . get_asset_gl_entry ( gl_entries )
make_gl_entries ( gl_entries , from_repost = from_repost )
2014-04-07 13:21:58 +00:00
2019-12-30 07:56:47 +00:00
def validate_serialized_batch ( self ) :
from erpnext . stock . doctype . serial_no . serial_no import get_serial_nos
for d in self . get ( " items " ) :
if hasattr ( d , ' serial_no ' ) and hasattr ( d , ' batch_no ' ) and d . serial_no and d . batch_no :
serial_nos = get_serial_nos ( d . serial_no )
for serial_no_data in frappe . get_all ( " Serial No " ,
filters = { " name " : ( " in " , serial_nos ) } , fields = [ " batch_no " , " name " ] ) :
if serial_no_data . batch_no != d . batch_no :
frappe . throw ( _ ( " Row # {0} : Serial No {1} does not belong to Batch {2} " )
. format ( d . idx , serial_no_data . name , d . batch_no ) )
2013-11-14 13:10:08 +00:00
def get_gl_entries ( self , warehouse_account = None , default_expense_account = None ,
2013-09-17 09:45:16 +00:00
default_cost_center = None ) :
2014-10-06 06:23:52 +00:00
2013-09-17 09:45:16 +00:00
if not warehouse_account :
2019-03-08 05:43:35 +00:00
warehouse_account = get_warehouse_account_map ( self . company )
2014-04-07 13:21:58 +00:00
2014-04-17 06:07:46 +00:00
sle_map = self . get_stock_ledger_details ( )
voucher_details = self . get_voucher_details ( default_expense_account , default_cost_center , sle_map )
2014-04-07 13:21:58 +00:00
2013-08-28 13:23:11 +00:00
gl_list = [ ]
2013-09-17 04:51:20 +00:00
warehouse_with_no_account = [ ]
2017-01-16 11:53:20 +00:00
2020-01-15 11:05:31 +00:00
precision = frappe . get_precision ( " GL Entry " , " debit_in_account_currency " )
2016-12-15 08:16:03 +00:00
for item_row in voucher_details :
sle_list = sle_map . get ( item_row . name )
2013-08-28 13:23:11 +00:00
if sle_list :
for sle in sle_list :
if warehouse_account . get ( sle . warehouse ) :
2020-02-18 06:58:41 +00:00
# from warehouse account/ target warehouse account
2017-01-16 11:53:20 +00:00
2016-12-15 08:16:03 +00:00
self . check_expense_account ( item_row )
2017-01-16 11:53:20 +00:00
2017-04-14 10:24:04 +00:00
# If the item does not have the allow zero valuation rate flag set
2017-02-06 11:43:39 +00:00
# and ( valuation rate not mentioned in an incoming entry
2017-03-31 07:14:29 +00:00
# or incoming entry not found while delivering the item),
2017-02-06 11:43:39 +00:00
# try to pick valuation rate from previous sle or Item master and update in SLE
# Otherwise, throw an exception
2017-02-06 19:53:26 +00:00
if not sle . stock_value_difference and self . doctype != " Stock Reconciliation " \
2017-04-14 10:24:04 +00:00
and not item_row . get ( " allow_zero_valuation_rate " ) :
2017-02-06 19:53:26 +00:00
2017-02-06 11:43:39 +00:00
sle = self . update_stock_ledger_entries ( sle )
2015-10-15 06:58:20 +00:00
2013-08-28 13:23:11 +00:00
gl_list . append ( self . get_gl_dict ( {
2017-06-15 05:39:27 +00:00
" account " : warehouse_account [ sle . warehouse ] [ " account " ] ,
2016-12-15 08:16:03 +00:00
" against " : item_row . expense_account ,
" cost_center " : item_row . cost_center ,
2014-04-07 06:32:57 +00:00
" remarks " : self . get ( " remarks " ) or " Accounting Entry for Stock " ,
2020-01-15 11:05:31 +00:00
" debit " : flt ( sle . stock_value_difference , precision ) ,
2019-05-22 12:33:09 +00:00
" is_opening " : item_row . get ( " is_opening " ) or self . get ( " is_opening " ) or " No " ,
2019-05-18 18:32:01 +00:00
} , warehouse_account [ sle . warehouse ] [ " account_currency " ] , item = item_row ) )
2013-08-28 13:23:11 +00:00
2020-02-18 06:58:41 +00:00
# expense account
2013-08-28 13:23:11 +00:00
gl_list . append ( self . get_gl_dict ( {
2016-12-15 08:16:03 +00:00
" account " : item_row . expense_account ,
2017-06-15 05:39:27 +00:00
" against " : warehouse_account [ sle . warehouse ] [ " account " ] ,
2016-12-15 08:16:03 +00:00
" cost_center " : item_row . cost_center ,
2014-04-07 06:32:57 +00:00
" remarks " : self . get ( " remarks " ) or " Accounting Entry for Stock " ,
2020-01-15 11:05:31 +00:00
" credit " : flt ( sle . stock_value_difference , precision ) ,
2019-05-16 11:58:39 +00:00
" project " : item_row . get ( " project " ) or self . get ( " project " ) ,
2019-05-22 12:33:09 +00:00
" is_opening " : item_row . get ( " is_opening " ) or self . get ( " is_opening " ) or " No "
2019-05-18 18:32:01 +00:00
} , item = item_row ) )
2013-09-17 04:51:20 +00:00
elif sle . warehouse not in warehouse_with_no_account :
warehouse_with_no_account . append ( sle . warehouse )
2014-04-07 13:21:58 +00:00
if warehouse_with_no_account :
2016-10-24 12:47:57 +00:00
for wh in warehouse_with_no_account :
if frappe . db . get_value ( " Warehouse " , wh , " company " ) :
2017-06-15 05:39:27 +00:00
frappe . throw ( _ ( " Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1} . " ) . format ( wh , self . company ) )
2015-10-15 06:58:20 +00:00
2013-08-28 13:23:11 +00:00
return process_gl_map ( gl_list )
2017-01-16 11:53:20 +00:00
2017-01-25 13:17:53 +00:00
def update_stock_ledger_entries ( self , sle ) :
2017-03-31 07:14:29 +00:00
sle . valuation_rate = get_valuation_rate ( sle . item_code , sle . warehouse ,
2017-06-16 09:51:36 +00:00
self . doctype , self . name , currency = self . company_currency , company = self . company )
2017-02-06 11:43:39 +00:00
2017-01-25 13:50:35 +00:00
sle . stock_value = flt ( sle . qty_after_transaction ) * flt ( sle . valuation_rate )
2017-02-06 11:43:39 +00:00
sle . stock_value_difference = flt ( sle . actual_qty ) * flt ( sle . valuation_rate )
2017-03-31 07:14:29 +00:00
2017-01-28 07:48:49 +00:00
if sle . name :
2017-02-06 11:43:39 +00:00
frappe . db . sql ( """
2017-03-31 07:14:29 +00:00
update
` tabStock Ledger Entry `
set
2017-02-06 11:43:39 +00:00
stock_value = % ( stock_value ) s ,
2017-03-31 07:14:29 +00:00
valuation_rate = % ( valuation_rate ) s ,
stock_value_difference = % ( stock_value_difference ) s
where
2017-02-06 11:43:39 +00:00
name = % ( name ) s """ , (sle))
2017-03-31 07:14:29 +00:00
2017-02-06 11:43:39 +00:00
return sle
2017-03-31 07:14:29 +00:00
2014-04-17 06:07:46 +00:00
def get_voucher_details ( self , default_expense_account , default_cost_center , sle_map ) :
if self . doctype == " Stock Reconciliation " :
2019-05-16 11:58:39 +00:00
reconciliation_purpose = frappe . db . get_value ( self . doctype , self . name , " purpose " )
is_opening = " Yes " if reconciliation_purpose == " Opening Stock " else " No "
details = [ ]
2019-07-03 05:04:31 +00:00
for voucher_detail_no in sle_map :
2019-05-16 11:58:39 +00:00
details . append ( frappe . _dict ( {
" name " : voucher_detail_no ,
" expense_account " : default_expense_account ,
" cost_center " : default_cost_center ,
" is_opening " : is_opening
} ) )
return details
2014-04-17 06:07:46 +00:00
else :
2014-12-26 07:45:21 +00:00
details = self . get ( " items " )
2014-04-17 06:07:46 +00:00
if default_expense_account or default_cost_center :
for d in details :
if default_expense_account and not d . get ( " expense_account " ) :
d . expense_account = default_expense_account
if default_cost_center and not d . get ( " cost_center " ) :
d . cost_center = default_cost_center
return details
2014-04-07 13:21:58 +00:00
2014-09-26 08:52:18 +00:00
def get_items_and_warehouses ( self ) :
2014-03-27 11:48:29 +00:00
items , warehouses = [ ] , [ ]
2014-04-07 13:21:58 +00:00
2014-12-26 07:45:21 +00:00
if hasattr ( self , " items " ) :
item_doclist = self . get ( " items " )
2014-03-28 08:25:00 +00:00
elif self . doctype == " Stock Reconciliation " :
2014-03-27 11:48:29 +00:00
import json
item_doclist = [ ]
2014-03-28 08:25:00 +00:00
data = json . loads ( self . reconciliation_json )
2014-03-27 11:48:29 +00:00
for row in data [ data . index ( self . head_row ) + 1 : ] :
d = frappe . _dict ( zip ( [ " item_code " , " warehouse " , " qty " , " valuation_rate " ] , row ) )
item_doclist . append ( d )
2014-04-07 13:21:58 +00:00
2014-03-27 11:48:29 +00:00
if item_doclist :
for d in item_doclist :
if d . item_code and d . item_code not in items :
items . append ( d . item_code )
2014-04-07 13:21:58 +00:00
2014-04-07 06:32:57 +00:00
if d . get ( " warehouse " ) and d . warehouse not in warehouses :
2014-03-27 11:48:29 +00:00
warehouses . append ( d . warehouse )
2014-04-07 13:21:58 +00:00
2014-04-07 06:32:57 +00:00
if self . doctype == " Stock Entry " :
if d . get ( " s_warehouse " ) and d . s_warehouse not in warehouses :
warehouses . append ( d . s_warehouse )
if d . get ( " t_warehouse " ) and d . t_warehouse not in warehouses :
warehouses . append ( d . t_warehouse )
2014-03-27 11:48:29 +00:00
2014-09-26 08:52:18 +00:00
return items , warehouses
2014-04-07 13:21:58 +00:00
2013-08-28 13:23:11 +00:00
def get_stock_ledger_details ( self ) :
stock_ledger = { }
2017-02-06 11:43:39 +00:00
stock_ledger_entries = frappe . db . sql ( """
2017-03-31 07:14:29 +00:00
select
2017-02-06 19:53:26 +00:00
name , warehouse , stock_value_difference , valuation_rate ,
2017-03-31 07:14:29 +00:00
voucher_detail_no , item_code , posting_date , posting_time ,
2017-02-06 19:53:26 +00:00
actual_qty , qty_after_transaction
2017-02-06 11:43:39 +00:00
from
` tabStock Ledger Entry `
where
voucher_type = % s and voucher_no = % s
""" , (self.doctype, self.name), as_dict=True)
2017-02-06 19:53:26 +00:00
2017-02-06 11:43:39 +00:00
for sle in stock_ledger_entries :
2013-08-28 13:23:11 +00:00
stock_ledger . setdefault ( sle . voucher_detail_no , [ ] ) . append ( sle )
return stock_ledger
2014-04-07 13:21:58 +00:00
2017-04-21 07:10:19 +00:00
def make_batches ( self , warehouse_field ) :
2017-04-20 09:51:01 +00:00
''' Create batches if required. Called before submit '''
for d in self . items :
2017-04-21 07:10:19 +00:00
if d . get ( warehouse_field ) and not d . batch_no :
has_batch_no , create_new_batch = frappe . db . get_value ( ' Item ' , d . item_code , [ ' has_batch_no ' , ' create_new_batch ' ] )
if has_batch_no and create_new_batch :
d . batch_no = frappe . get_doc ( dict (
doctype = ' Batch ' ,
item = d . item_code ,
supplier = getattr ( self , ' supplier ' , None ) ,
reference_doctype = self . doctype ,
reference_name = self . name ) ) . insert ( ) . name
2017-04-20 09:51:01 +00:00
2013-08-26 11:23:30 +00:00
def check_expense_account ( self , item ) :
2014-04-16 13:50:11 +00:00
if not item . get ( " expense_account " ) :
2014-05-01 12:12:21 +00:00
frappe . throw ( _ ( " Expense or Difference account is mandatory for Item {0} as it impacts overall stock value " ) . format ( item . item_code ) )
2014-04-07 13:21:58 +00:00
2014-06-19 13:55:19 +00:00
else :
2014-06-25 13:42:24 +00:00
is_expense_account = frappe . db . get_value ( " Account " ,
item . get ( " expense_account " ) , " report_type " ) == " Profit and Loss "
2016-02-08 16:44:55 +00:00
if self . doctype not in ( " Purchase Receipt " , " Purchase Invoice " , " Stock Reconciliation " , " Stock Entry " ) and not is_expense_account :
2014-06-25 13:42:24 +00:00
frappe . throw ( _ ( " Expense / Difference account ( {0} ) must be a ' Profit or Loss ' account " )
. format ( item . get ( " expense_account " ) ) )
2014-06-19 13:55:19 +00:00
if is_expense_account and not item . get ( " cost_center " ) :
frappe . throw ( _ ( " {0} {1} : Cost Center is mandatory for Item {2} " ) . format (
_ ( self . doctype ) , self . name , item . get ( " item_code " ) ) )
2014-04-07 13:21:58 +00:00
2020-01-23 12:06:52 +00:00
def delete_auto_created_batches ( self ) :
for d in self . items :
if not d . batch_no : continue
2020-03-17 11:34:18 +00:00
serial_nos = [ sr . name for sr in frappe . get_all ( " Serial No " , { ' batch_no ' : d . batch_no } ) ]
2020-03-02 09:32:58 +00:00
if serial_nos :
frappe . db . set_value ( " Serial No " , { ' name ' : [ ' in ' , serial_nos ] } , " batch_no " , None )
2020-01-23 12:06:52 +00:00
d . batch_no = None
d . db_set ( " batch_no " , None )
for data in frappe . get_all ( " Batch " ,
{ ' reference_name ' : self . name , ' reference_doctype ' : self . doctype } ) :
frappe . delete_doc ( " Batch " , data . name )
2020-01-23 07:12:42 +00:00
2014-04-07 13:21:58 +00:00
def get_sl_entries ( self , d , args ) :
2014-11-03 09:38:21 +00:00
sl_dict = frappe . _dict ( {
2014-05-02 12:06:13 +00:00
" item_code " : d . get ( " item_code " , None ) ,
2014-04-07 06:32:57 +00:00
" warehouse " : d . get ( " warehouse " , None ) ,
2014-03-28 08:25:00 +00:00
" posting_date " : self . posting_date ,
" posting_time " : self . posting_time ,
2016-03-04 09:03:49 +00:00
' fiscal_year ' : get_fiscal_year ( self . posting_date , company = self . company ) [ 0 ] ,
2014-03-28 08:25:00 +00:00
" voucher_type " : self . doctype ,
" voucher_no " : self . name ,
2013-08-02 06:12:11 +00:00
" voucher_detail_no " : d . name ,
2014-04-07 06:32:57 +00:00
" actual_qty " : ( self . docstatus == 1 and 1 or - 1 ) * flt ( d . get ( " stock_qty " ) ) ,
2015-09-21 03:48:43 +00:00
" stock_uom " : frappe . db . get_value ( " Item " , args . get ( " item_code " ) or d . get ( " item_code " ) , " stock_uom " ) ,
2013-08-02 06:12:11 +00:00
" incoming_rate " : 0 ,
2014-03-28 08:25:00 +00:00
" company " : self . company ,
2014-05-02 12:06:13 +00:00
" batch_no " : cstr ( d . get ( " batch_no " ) ) . strip ( ) ,
" serial_no " : d . get ( " serial_no " ) ,
2018-03-21 12:22:41 +00:00
" project " : d . get ( " project " ) or self . get ( ' project ' ) ,
2014-03-28 08:25:00 +00:00
" is_cancelled " : self . docstatus == 2 and " Yes " or " No "
2014-11-03 09:38:21 +00:00
} )
2014-04-07 13:21:58 +00:00
2013-08-02 06:12:11 +00:00
sl_dict . update ( args )
return sl_dict
2014-04-07 13:21:58 +00:00
2015-04-06 13:59:16 +00:00
def make_sl_entries ( self , sl_entries , is_amended = None , allow_negative_stock = False ,
2015-03-27 10:08:31 +00:00
via_landed_cost_voucher = False ) :
2013-12-12 13:42:19 +00:00
from erpnext . stock . stock_ledger import make_sl_entries
2015-03-27 10:08:31 +00:00
make_sl_entries ( sl_entries , is_amended , allow_negative_stock , via_landed_cost_voucher )
2014-04-07 13:21:58 +00:00
2016-08-19 11:09:33 +00:00
def make_gl_entries_on_cancel ( self , repost_future_gle = True ) :
2014-04-07 13:21:58 +00:00
if frappe . db . sql ( """ select name from `tabGL Entry` where voucher_type= %s
2014-03-28 08:25:00 +00:00
and voucher_no = % s """ , (self.doctype, self.name)):
2016-12-30 10:51:35 +00:00
self . make_gl_entries ( repost_future_gle = repost_future_gle )
2014-04-07 13:21:58 +00:00
2014-06-25 08:01:02 +00:00
def get_serialized_items ( self ) :
serialized_items = [ ]
2014-12-26 07:45:21 +00:00
item_codes = list ( set ( [ d . item_code for d in self . get ( " items " ) ] ) )
2014-06-25 08:01:02 +00:00
if item_codes :
serialized_items = frappe . db . sql_list ( """ select name from `tabItem`
2015-07-24 09:46:25 +00:00
where has_serial_no = 1 and name in ( { } ) """ .format( " , " .join([ " %s " ]*len(item_codes))),
2014-06-25 08:01:02 +00:00
tuple ( item_codes ) )
return serialized_items
2015-08-03 10:43:33 +00:00
2020-02-18 06:58:41 +00:00
def get_incoming_rate_for_return ( self , item_code , against_document ) :
2015-07-17 09:49:02 +00:00
incoming_rate = 0.0
if against_document and item_code :
2015-11-16 13:35:46 +00:00
incoming_rate = frappe . db . sql ( """ select abs(stock_value_difference / actual_qty)
2015-07-17 09:49:02 +00:00
from ` tabStock Ledger Entry `
2015-10-15 06:58:20 +00:00
where voucher_type = % s and voucher_no = % s
2016-01-19 12:35:28 +00:00
and item_code = % s limit 1 """ ,
( self . doctype , against_document , item_code ) )
2015-07-17 09:49:02 +00:00
incoming_rate = incoming_rate [ 0 ] [ 0 ] if incoming_rate else 0.0
return incoming_rate
2017-01-16 11:53:20 +00:00
2015-11-18 11:33:33 +00:00
def validate_warehouse ( self ) :
from erpnext . stock . utils import validate_warehouse_company
warehouses = list ( set ( [ d . warehouse for d in
self . get ( " items " ) if getattr ( d , " warehouse " , None ) ] ) )
2020-02-18 06:58:41 +00:00
target_warehouses = list ( set ( [ d . target_warehouse for d in
self . get ( " items " ) if getattr ( d , " target_warehouse " , None ) ] ) )
warehouses . extend ( target_warehouses )
from_warehouse = list ( set ( [ d . from_warehouse for d in
self . get ( " items " ) if getattr ( d , " from_warehouse " , None ) ] ) )
warehouses . e xtend ( from_warehouse )
2015-11-18 11:33:33 +00:00
for w in warehouses :
validate_warehouse_company ( w , self . company )
2017-01-16 11:53:20 +00:00
2016-01-06 11:02:06 +00:00
def update_billing_percentage ( self , update_modified = True ) :
2015-12-30 13:38:11 +00:00
self . _update_percent_field ( {
" target_dt " : self . doctype + " Item " ,
" target_parent_dt " : self . doctype ,
" target_parent_field " : " per_billed " ,
" target_ref_field " : " amount " ,
" target_field " : " billed_amt " ,
" name " : self . name ,
2016-01-06 11:02:06 +00:00
} , update_modified )
2014-06-25 08:01:02 +00:00
2016-11-16 11:51:59 +00:00
def validate_inspection ( self ) :
2016-11-10 13:45:11 +00:00
''' Checks if quality inspection is set for Items that require inspection.
On submit , throw an exception '''
2016-11-18 09:22:13 +00:00
inspection_required_fieldname = None
2016-11-16 11:51:59 +00:00
if self . doctype in [ " Purchase Receipt " , " Purchase Invoice " ] :
inspection_required_fieldname = " inspection_required_before_purchase "
elif self . doctype in [ " Delivery Note " , " Sales Invoice " ] :
inspection_required_fieldname = " inspection_required_before_delivery "
2017-01-16 11:53:20 +00:00
2018-02-22 05:33:48 +00:00
if ( ( not inspection_required_fieldname and self . doctype != " Stock Entry " ) or
( self . doctype == " Stock Entry " and not self . inspection_required ) or
( self . doctype in [ " Sales Invoice " , " Purchase Invoice " ] and not self . update_stock ) ) :
2016-11-16 11:51:59 +00:00
return
2017-01-16 11:53:20 +00:00
2016-11-10 13:45:11 +00:00
for d in self . get ( ' items ' ) :
2018-12-24 09:24:42 +00:00
qa_required = False
2018-02-22 05:33:48 +00:00
if ( inspection_required_fieldname and not d . quality_inspection and
frappe . db . get_value ( " Item " , d . item_code , inspection_required_fieldname ) ) :
2018-12-24 09:24:42 +00:00
qa_required = True
2018-02-22 05:33:48 +00:00
elif self . doctype == " Stock Entry " and not d . quality_inspection and d . t_warehouse :
2018-12-24 09:24:42 +00:00
qa_required = True
2018-12-28 11:23:00 +00:00
if self . docstatus == 1 and d . quality_inspection :
qa_doc = frappe . get_doc ( " Quality Inspection " , d . quality_inspection )
if qa_doc . docstatus == 0 :
link = frappe . utils . get_link_to_form ( ' Quality Inspection ' , d . quality_inspection )
2019-01-01 09:30:16 +00:00
frappe . throw ( _ ( " Quality Inspection: {0} is not submitted for the item: {1} in row {2} " ) . format ( link , d . item_code , d . idx ) , QualityInspectionNotSubmittedError )
2018-12-28 11:23:00 +00:00
qa_failed = any ( [ r . status == " Rejected " for r in qa_doc . readings ] )
if qa_failed :
frappe . throw ( _ ( " Row {0} : Quality Inspection rejected for item {1} " )
. format ( d . idx , d . item_code ) , QualityInspectionRejectedError )
elif qa_required :
2019-07-03 05:04:31 +00:00
action = frappe . get_doc ( ' Stock Settings ' ) . action_if_quality_inspection_is_not_submitted
if self . docstatus == 1 and action == ' Stop ' :
frappe . throw ( _ ( " Quality Inspection required for Item {0} to submit " ) . format ( frappe . bold ( d . item_code ) ) ,
exc = QualityInspectionRequiredError )
else :
frappe . msgprint ( _ ( " Create Quality Inspection for Item {0} " ) . format ( frappe . bold ( d . item_code ) ) )
2016-11-10 13:45:11 +00:00
2018-06-14 10:24:34 +00:00
def update_blanket_order ( self ) :
2018-06-14 11:39:55 +00:00
blanket_orders = list ( set ( [ d . blanket_order for d in self . items if d . blanket_order ] ) )
2018-06-14 10:24:34 +00:00
for blanket_order in blanket_orders :
frappe . get_doc ( " Blanket Order " , blanket_order ) . update_ordered_qty ( )
2018-05-28 14:37:08 +00:00
2020-04-03 10:16:48 +00:00
def validate_customer_provided_item ( self ) :
for d in self . get ( ' items ' ) :
# Customer Provided parts will have zero valuation rate
if frappe . db . get_value ( ' Item ' , d . item_code , ' is_customer_provided_item ' ) :
d . allow_zero_valuation_rate = 1
2014-10-06 06:23:52 +00:00
def update_gl_entries_after ( posting_date , posting_time , for_warehouses = None , for_items = None ,
2019-03-08 05:43:35 +00:00
warehouse_account = None , company = None ) :
2014-03-27 11:48:29 +00:00
def _delete_gl_entries ( voucher_type , voucher_no ) :
2014-04-07 13:21:58 +00:00
frappe . db . sql ( """ delete from `tabGL Entry`
2014-03-27 11:48:29 +00:00
where voucher_type = % s and voucher_no = % s """ , (voucher_type, voucher_no))
2014-04-07 13:21:58 +00:00
2014-03-27 11:48:29 +00:00
if not warehouse_account :
2019-03-08 05:43:35 +00:00
warehouse_account = get_warehouse_account_map ( company )
2014-09-26 08:52:18 +00:00
future_stock_vouchers = get_future_stock_vouchers ( posting_date , posting_time , for_warehouses , for_items )
2014-03-27 11:48:29 +00:00
gle = get_voucherwise_gl_entries ( future_stock_vouchers , posting_date )
2017-01-16 11:53:20 +00:00
2014-03-27 11:48:29 +00:00
for voucher_type , voucher_no in future_stock_vouchers :
existing_gle = gle . get ( ( voucher_type , voucher_no ) , [ ] )
2014-03-31 11:57:06 +00:00
voucher_obj = frappe . get_doc ( voucher_type , voucher_no )
2014-03-27 11:48:29 +00:00
expected_gle = voucher_obj . get_gl_entries ( warehouse_account )
if expected_gle :
2016-08-08 08:42:16 +00:00
if not existing_gle or not compare_existing_and_expected_gle ( existing_gle , expected_gle ) :
_delete_gl_entries ( voucher_type , voucher_no )
2016-12-30 10:51:35 +00:00
voucher_obj . make_gl_entries ( gl_entries = expected_gle , repost_future_gle = False , from_repost = True )
2014-03-27 11:48:29 +00:00
else :
_delete_gl_entries ( voucher_type , voucher_no )
2014-04-07 13:21:58 +00:00
2014-03-27 11:48:29 +00:00
def compare_existing_and_expected_gle ( existing_gle , expected_gle ) :
matched = True
for entry in expected_gle :
2016-09-12 03:17:14 +00:00
account_existed = False
2014-03-27 11:48:29 +00:00
for e in existing_gle :
2016-09-12 03:17:14 +00:00
if entry . account == e . account :
account_existed = True
if entry . account == e . account and entry . against_account == e . against_account \
and ( not entry . cost_center or not e . cost_center or entry . cost_center == e . cost_center ) \
and ( entry . debit != e . debit or entry . credit != e . credit ) :
matched = False
break
if not account_existed :
matched = False
break
2014-03-27 11:48:29 +00:00
return matched
2013-03-19 06:31:24 +00:00
2014-09-26 08:52:18 +00:00
def get_future_stock_vouchers ( posting_date , posting_time , for_warehouses = None , for_items = None ) :
2014-03-27 11:48:29 +00:00
future_stock_vouchers = [ ]
2014-04-07 13:21:58 +00:00
2014-08-27 16:39:03 +00:00
values = [ ]
2014-03-27 11:48:29 +00:00
condition = " "
if for_items :
2014-08-27 16:39:03 +00:00
condition + = " and item_code in ( {} ) " . format ( " , " . join ( [ " %s " ] * len ( for_items ) ) )
values + = for_items
2014-04-07 13:21:58 +00:00
2014-09-26 08:52:18 +00:00
if for_warehouses :
condition + = " and warehouse in ( {} ) " . format ( " , " . join ( [ " %s " ] * len ( for_warehouses ) ) )
values + = for_warehouses
2014-04-07 13:21:58 +00:00
for d in frappe . db . sql ( """ select distinct sle.voucher_type, sle.voucher_no
2014-03-27 11:48:29 +00:00
from ` tabStock Ledger Entry ` sle
2014-08-27 16:39:03 +00:00
where timestamp ( sle . posting_date , sle . posting_time ) > = timestamp ( % s , % s ) { condition }
2020-02-26 13:21:13 +00:00
order by timestamp ( sle . posting_date , sle . posting_time ) asc , creation asc for update """ .format(condition=condition),
2014-08-27 16:39:03 +00:00
tuple ( [ posting_date , posting_time ] + values ) , as_dict = True ) :
2014-03-27 11:48:29 +00:00
future_stock_vouchers . append ( [ d . voucher_type , d . voucher_no ] )
2014-04-07 13:21:58 +00:00
2014-03-27 11:48:29 +00:00
return future_stock_vouchers
2014-04-07 13:21:58 +00:00
2014-03-27 11:48:29 +00:00
def get_voucherwise_gl_entries ( future_stock_vouchers , posting_date ) :
gl_entries = { }
if future_stock_vouchers :
2014-04-07 13:21:58 +00:00
for d in frappe . db . sql ( """ select * from `tabGL Entry`
where posting_date > = % s and voucher_no in ( % s ) """ %
( ' %s ' , ' , ' . join ( [ ' %s ' ] * len ( future_stock_vouchers ) ) ) ,
2014-03-27 11:48:29 +00:00
tuple ( [ posting_date ] + [ d [ 1 ] for d in future_stock_vouchers ] ) , as_dict = 1 ) :
gl_entries . setdefault ( ( d . voucher_type , d . voucher_no ) , [ ] ) . append ( d )
2014-04-07 13:21:58 +00:00
2019-01-01 09:30:16 +00:00
return gl_entries