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-06-03 11:15:38 +00:00
from __future__ import unicode_literals
2014-02-14 10:17:51 +00:00
import frappe
2017-04-07 11:46:16 +00:00
from frappe . utils import flt , comma_or , nowdate , getdate
2016-07-07 08:32:26 +00:00
from frappe import _
2014-04-21 09:36:56 +00:00
from frappe . model . document import Document
2013-06-03 11:15:38 +00:00
2019-07-15 12:32:58 +00:00
class OverAllowanceError ( frappe . ValidationError ) : pass
2015-05-18 05:48:55 +00:00
def validate_status ( status , options ) :
if status not in options :
frappe . throw ( _ ( " Status must be one of {0} " ) . format ( comma_or ( options ) ) )
2013-10-03 11:56:33 +00:00
status_map = {
" Lead " : [
2016-12-15 06:21:54 +00:00
[ " Lost Quotation " , " has_lost_quotation " ] ,
2013-10-03 11:56:33 +00:00
[ " Opportunity " , " has_opportunity " ] ,
2016-06-09 18:19:51 +00:00
[ " Quotation " , " has_quotation " ] ,
[ " Converted " , " has_customer " ] ,
2013-10-03 11:56:33 +00:00
] ,
" Opportunity " : [
2016-03-29 05:43:07 +00:00
[ " Lost " , " eval:self.status== ' Lost ' " ] ,
2016-06-09 18:19:51 +00:00
[ " Lost " , " has_lost_quotation " ] ,
2017-05-30 10:04:20 +00:00
[ " Quotation " , " has_active_quotation " ] ,
[ " Converted " , " has_ordered_quotation " ] ,
2016-03-29 05:43:07 +00:00
[ " Closed " , " eval:self.status== ' Closed ' " ]
2013-10-03 11:56:33 +00:00
] ,
" Quotation " : [
2015-04-13 11:26:03 +00:00
[ " Draft " , None ] ,
2019-06-26 05:35:51 +00:00
[ " Open " , " eval:self.docstatus==1 " ] ,
2014-03-28 08:25:00 +00:00
[ " Lost " , " eval:self.status== ' Lost ' " ] ,
2013-10-03 11:56:33 +00:00
[ " Ordered " , " has_sales_order " ] ,
2014-03-28 08:25:00 +00:00
[ " Cancelled " , " eval:self.docstatus==2 " ] ,
2013-10-03 11:56:33 +00:00
] ,
" Sales Order " : [
[ " Draft " , None ] ,
2015-10-02 07:12:48 +00:00
[ " To Deliver and Bill " , " eval:self.per_delivered < 100 and self.per_billed < 100 and self.docstatus == 1 " ] ,
2019-10-12 14:03:11 +00:00
[ " To Bill " , " eval:(self.per_delivered == 100 or self.skip_delivery_note) and self.per_billed < 100 and self.docstatus == 1 " ] ,
[ " To Deliver " , " eval:self.per_delivered < 100 and self.per_billed == 100 and self.docstatus == 1 and not self.skip_delivery_note " ] ,
[ " Completed " , " eval:(self.per_delivered == 100 or self.skip_delivery_note) and self.per_billed == 100 and self.docstatus == 1 " ] ,
2015-10-02 07:12:48 +00:00
[ " Cancelled " , " eval:self.docstatus==2 " ] ,
2015-10-19 08:47:52 +00:00
[ " Closed " , " eval:self.status== ' Closed ' " ] ,
2019-03-01 10:53:27 +00:00
[ " On Hold " , " eval:self.status== ' On Hold ' " ] ,
2015-10-02 07:12:48 +00:00
] ,
" Purchase Order " : [
[ " Draft " , None ] ,
[ " To Receive and Bill " , " eval:self.per_received < 100 and self.per_billed < 100 and self.docstatus == 1 " ] ,
2019-09-05 09:41:43 +00:00
[ " To Bill " , " eval:self.per_received >= 100 and self.per_billed < 100 and self.docstatus == 1 " ] ,
2015-10-02 07:12:48 +00:00
[ " To Receive " , " eval:self.per_received < 100 and self.per_billed == 100 and self.docstatus == 1 " ] ,
2019-09-05 09:41:43 +00:00
[ " Completed " , " eval:self.per_received >= 100 and self.per_billed == 100 and self.docstatus == 1 " ] ,
2015-10-23 05:10:32 +00:00
[ " Delivered " , " eval:self.status== ' Delivered ' " ] ,
2014-03-28 08:25:00 +00:00
[ " Cancelled " , " eval:self.docstatus==2 " ] ,
2019-03-11 11:10:27 +00:00
[ " On Hold " , " eval:self.status== ' On Hold ' " ] ,
2015-10-19 09:46:17 +00:00
[ " Closed " , " eval:self.status== ' Closed ' " ] ,
2013-10-03 11:56:33 +00:00
] ,
2015-05-18 05:48:55 +00:00
" Delivery Note " : [
[ " Draft " , None ] ,
2015-12-28 07:33:55 +00:00
[ " To Bill " , " eval:self.per_billed < 100 and self.docstatus == 1 " ] ,
[ " Completed " , " eval:self.per_billed == 100 and self.docstatus == 1 " ] ,
2015-05-18 05:48:55 +00:00
[ " Cancelled " , " eval:self.docstatus==2 " ] ,
2015-11-02 07:27:08 +00:00
[ " Closed " , " eval:self.status== ' Closed ' " ] ,
2015-05-18 05:48:55 +00:00
] ,
" Purchase Receipt " : [
[ " Draft " , None ] ,
2015-12-30 13:38:11 +00:00
[ " To Bill " , " eval:self.per_billed < 100 and self.docstatus == 1 " ] ,
[ " Completed " , " eval:self.per_billed == 100 and self.docstatus == 1 " ] ,
2015-05-18 05:48:55 +00:00
[ " Cancelled " , " eval:self.docstatus==2 " ] ,
2015-11-02 07:27:08 +00:00
[ " Closed " , " eval:self.status== ' Closed ' " ] ,
2017-06-07 06:32:07 +00:00
] ,
2020-04-08 10:08:28 +00:00
" Purchase Invoice " : [
[ " Draft " , None ] ,
[ " Submitted " , " eval:self.docstatus==1 " ] ,
[ " Paid " , " eval:self.outstanding_amount==0 and self.docstatus==1 " ] ,
[ " Return " , " eval:self.is_return==1 and self.docstatus==1 " ] ,
[ " Debit Note Issued " ,
" eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value( ' Purchase Invoice ' , { ' is_return ' : 1, ' return_against ' : self.name, ' docstatus ' : 1}) " ] ,
[ " Unpaid " , " eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1 " ] ,
[ " Overdue " , " eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1 " ] ,
[ " Cancelled " , " eval:self.docstatus==2 " ] ,
2020-03-17 11:34:18 +00:00
] ,
2017-06-07 06:32:07 +00:00
" Material Request " : [
[ " Draft " , None ] ,
[ " Stopped " , " eval:self.status == ' Stopped ' " ] ,
[ " Cancelled " , " eval:self.docstatus == 2 " ] ,
[ " Pending " , " eval:self.status != ' Stopped ' and self.per_ordered == 0 and self.docstatus == 1 " ] ,
[ " Partially Ordered " , " eval:self.status != ' Stopped ' and self.per_ordered < 100 and self.per_ordered > 0 and self.docstatus == 1 " ] ,
[ " Ordered " , " eval:self.status != ' Stopped ' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == ' Purchase ' " ] ,
[ " Transferred " , " eval:self.status != ' Stopped ' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == ' Material Transfer ' " ] ,
2019-04-22 06:55:14 +00:00
[ " Issued " , " eval:self.status != ' Stopped ' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == ' Material Issue ' " ] ,
2019-04-29 16:12:48 +00:00
[ " Received " , " eval:self.status != ' Stopped ' and self.per_received == 100 and self.docstatus == 1 and self.material_request_type == ' Purchase ' " ] ,
2019-07-22 06:05:16 +00:00
[ " Partially Received " , " eval:self.status != ' Stopped ' and self.per_received > 0 and self.per_received < 100 and self.docstatus == 1 and self.material_request_type == ' Purchase ' " ] ,
[ " Manufactured " , " eval:self.status != ' Stopped ' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == ' Manufacture ' " ]
2019-07-03 05:04:31 +00:00
] ,
" Bank Transaction " : [
[ " Unreconciled " , " eval:self.docstatus == 1 and self.unallocated_amount>0 " ] ,
[ " Reconciled " , " eval:self.docstatus == 1 and self.unallocated_amount<=0 " ]
2015-05-18 05:48:55 +00:00
]
2013-10-03 11:56:33 +00:00
}
2014-04-21 09:36:56 +00:00
class StatusUpdater ( Document ) :
2013-06-03 11:15:38 +00:00
"""
Updates the status of the calling records
Delivery Note : Update Delivered Qty , Update Percent and Validate over delivery
Sales Invoice : Update Billed Amt , Update Percent and Validate over billing
Installation Note : Update Installed Qty , Update Percent Qty and Validate over installation
"""
def update_prevdoc_status ( self ) :
self . update_qty ( )
self . validate_qty ( )
2014-04-09 13:50:01 +00:00
2015-12-08 08:32:53 +00:00
def set_status ( self , update = False , status = None , update_modified = True ) :
2014-08-19 10:50:04 +00:00
if self . is_new ( ) :
2017-01-13 07:21:19 +00:00
if self . get ( ' amended_from ' ) :
self . status = ' Draft '
2013-10-03 11:56:33 +00:00
return
2016-01-06 11:02:06 +00:00
2014-03-28 08:25:00 +00:00
if self . doctype in status_map :
2014-08-21 11:04:38 +00:00
_status = self . status
2015-11-04 09:50:50 +00:00
if status and update :
self . db_set ( " status " , status )
2014-03-28 08:25:00 +00:00
sl = status_map [ self . doctype ] [ : ]
2013-10-03 12:42:36 +00:00
sl . reverse ( )
for s in sl :
2013-10-03 11:56:33 +00:00
if not s [ 1 ] :
2014-03-28 08:25:00 +00:00
self . status = s [ 0 ]
2013-10-03 11:56:33 +00:00
break
2013-10-03 12:42:36 +00:00
elif s [ 1 ] . startswith ( " eval: " ) :
2018-09-06 12:57:06 +00:00
if frappe . safe_eval ( s [ 1 ] [ 5 : ] , None , { " self " : self . as_dict ( ) , " getdate " : getdate ,
2017-05-16 07:13:00 +00:00
" nowdate " : nowdate , " get_value " : frappe . db . get_value } ) :
2014-03-28 08:25:00 +00:00
self . status = s [ 0 ]
2013-10-03 11:56:33 +00:00
break
elif getattr ( self , s [ 1 ] ) ( ) :
2014-03-28 08:25:00 +00:00
self . status = s [ 0 ]
2013-10-03 11:56:33 +00:00
break
2014-04-09 13:50:01 +00:00
2017-06-07 06:32:07 +00:00
if self . status != _status and self . status not in ( " Cancelled " , " Partially Ordered " ,
" Ordered " , " Issued " , " Transferred " ) :
2015-01-07 10:11:32 +00:00
self . add_comment ( " Label " , _ ( self . status ) )
2016-01-06 11:02:06 +00:00
2013-10-03 11:56:33 +00:00
if update :
2016-07-20 06:38:47 +00:00
self . db_set ( ' status ' , self . status , update_modified = update_modified )
2014-04-09 13:50:01 +00:00
2013-06-03 11:15:38 +00:00
def validate_qty ( self ) :
2014-12-19 10:50:32 +00:00
""" Validates qty at row level """
2019-07-15 12:32:58 +00:00
self . item_allowance = { }
self . global_qty_allowance = None
self . global_amount_allowance = None
2014-04-09 13:50:01 +00:00
2013-06-03 11:15:38 +00:00
for args in self . status_updater :
2015-08-25 07:19:40 +00:00
if " target_ref_field " not in args :
# if target_ref_field is not specified, the programmer does not want to validate qty / amount
continue
2013-06-03 11:15:38 +00:00
# get unique transactions to update
2014-04-02 12:39:34 +00:00
for d in self . get_all_children ( ) :
2018-03-12 05:50:30 +00:00
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 ) )
2018-06-11 06:32:14 +00:00
if hasattr ( d , ' qty ' ) and d . qty > 0 and self . get ( ' is_return ' ) :
frappe . throw ( _ ( " For an item {0} , quantity must be negative number " ) . format ( d . item_code ) )
2014-03-28 08:25:00 +00:00
if d . doctype == args [ ' source_dt ' ] and d . get ( args [ " join_field " ] ) :
2014-03-31 18:07:40 +00:00
args [ ' name ' ] = d . get ( args [ ' join_field ' ] )
2013-06-03 11:15:38 +00:00
# get all qty where qty > target_field
2014-04-09 13:50:01 +00:00
item = frappe . db . sql ( """ select item_code, ` {target_ref_field} `,
` { target_field } ` , parenttype , parent from ` tab { target_dt } `
where ` { target_ref_field } ` < ` { target_field } `
and name = % s and docstatus = 1 """ .format(**args),
2014-03-03 10:21:13 +00:00
args [ ' name ' ] , as_dict = 1 )
2013-06-03 11:15:38 +00:00
if item :
item = item [ 0 ]
item [ ' idx ' ] = d . idx
item [ ' target_ref_field ' ] = args [ ' target_ref_field ' ] . replace ( ' _ ' , ' ' )
2016-07-07 08:32:26 +00:00
# if not item[args['target_ref_field']]:
# msgprint(_("Note: System will not check over-delivery and over-booking for Item {0} as quantity or amount is 0").format(item.item_code))
2019-07-15 12:32:58 +00:00
if args . get ( ' no_allowance ' ) :
2014-07-09 12:06:38 +00:00
item [ ' reduce_by ' ] = item [ args [ ' target_field ' ] ] - item [ args [ ' target_ref_field ' ] ]
2013-06-03 11:15:38 +00:00
if item [ ' reduce_by ' ] > .01 :
2020-01-15 13:52:35 +00:00
self . limits_crossed_error ( args , item , " qty " )
2014-04-09 13:50:01 +00:00
2016-08-26 06:09:23 +00:00
elif item [ args [ ' target_ref_field ' ] ] :
2019-07-15 12:32:58 +00:00
self . check_overflow_with_allowance ( item , args )
2014-04-09 13:50:01 +00:00
2019-07-15 12:32:58 +00:00
def check_overflow_with_allowance ( self , item , args ) :
2013-06-03 11:15:38 +00:00
"""
2019-07-15 12:32:58 +00:00
Checks if there is overflow condering a relaxation allowance
2013-06-03 11:15:38 +00:00
"""
2019-07-15 12:32:58 +00:00
qty_or_amount = " qty " if " qty " in args [ ' target_ref_field ' ] else " amount "
# check if overflow is within allowance
allowance , self . item_allowance , self . global_qty_allowance , self . global_amount_allowance = \
get_allowance_for ( item [ ' item_code ' ] , self . item_allowance ,
self . global_qty_allowance , self . global_amount_allowance , qty_or_amount )
2014-04-09 13:50:01 +00:00
overflow_percent = ( ( item [ args [ ' target_field ' ] ] - item [ args [ ' target_ref_field ' ] ] ) /
2013-06-03 11:15:38 +00:00
item [ args [ ' target_ref_field ' ] ] ) * 100
2014-07-19 11:30:15 +00:00
2019-07-15 12:32:58 +00:00
if overflow_percent - allowance > 0.01 :
item [ ' max_allowed ' ] = flt ( item [ args [ ' target_ref_field ' ] ] * ( 100 + allowance ) / 100 )
2013-06-03 11:15:38 +00:00
item [ ' reduce_by ' ] = item [ args [ ' target_field ' ] ] - item [ ' max_allowed ' ]
2014-04-09 13:50:01 +00:00
2019-07-15 12:32:58 +00:00
self . limits_crossed_error ( args , item , qty_or_amount )
2016-07-07 08:32:26 +00:00
2019-07-15 12:32:58 +00:00
def limits_crossed_error ( self , args , item , qty_or_amount ) :
2016-07-07 08:32:26 +00:00
''' Raise exception for limits crossed '''
2019-07-15 12:32:58 +00:00
if qty_or_amount == " qty " :
action_msg = _ ( ' To allow over receipt / delivery, update " Over Receipt/Delivery Allowance " in Stock Settings or the Item. ' )
else :
action_msg = _ ( ' To allow over billing, update " Over Billing Allowance " in Accounts Settings or the Item. ' )
2016-07-07 08:32:26 +00:00
frappe . throw ( _ ( ' This document is over limit by {0} {1} for item {4} . Are you making another {3} against the same {2} ? ' )
. format (
frappe . bold ( _ ( item [ " target_ref_field " ] . title ( ) ) ) ,
frappe . bold ( item [ " reduce_by " ] ) ,
frappe . bold ( _ ( args . get ( ' target_dt ' ) ) ) ,
frappe . bold ( _ ( self . doctype ) ) ,
frappe . bold ( item . get ( ' item_code ' ) )
2019-07-15 12:32:58 +00:00
) + ' <br><br> ' + action_msg , OverAllowanceError , title = _ ( ' Limit Crossed ' ) )
2013-06-03 11:15:38 +00:00
2016-01-06 11:02:06 +00:00
def update_qty ( self , update_modified = True ) :
2015-08-25 07:19:40 +00:00
""" Updates qty or amount at row level
2016-01-06 11:02:06 +00:00
: param update_modified : If true , updates ` modified ` and ` modified_by ` for target parent doc
2013-06-03 11:15:38 +00:00
"""
for args in self . status_updater :
# condition to include current record (if submit or no if cancel)
2014-03-28 08:25:00 +00:00
if self . docstatus == 1 :
args [ ' cond ' ] = ' or parent= " %s " ' % self . name . replace ( ' " ' , ' \" ' )
2013-06-03 11:15:38 +00:00
else :
2014-03-28 08:25:00 +00:00
args [ ' cond ' ] = ' and parent!= " %s " ' % self . name . replace ( ' " ' , ' \" ' )
2014-04-09 13:50:01 +00:00
2016-01-06 11:02:06 +00:00
self . _update_children ( args , update_modified )
2015-08-25 07:19:40 +00:00
if " percent_join_field " in args :
2016-01-06 11:02:06 +00:00
self . _update_percent_field_in_targets ( args , update_modified )
2015-08-25 07:19:40 +00:00
2016-01-06 11:02:06 +00:00
def _update_children ( self , args , update_modified ) :
2015-08-25 07:19:40 +00:00
""" Update quantities or amount in child table """
for d in self . get_all_children ( ) :
if d . doctype != args [ ' source_dt ' ] :
continue
2016-01-06 11:02:06 +00:00
self . _update_modified ( args , update_modified )
2015-08-25 07:19:40 +00:00
# updates qty in the child table
args [ ' detail_id ' ] = d . get ( args [ ' join_field ' ] )
args [ ' second_source_condition ' ] = " "
if args . get ( ' second_source_dt ' ) and args . get ( ' second_source_field ' ) \
and args . get ( ' second_join_field ' ) :
if not args . get ( " second_source_extra_cond " ) :
args [ " second_source_extra_cond " ] = " "
args [ ' second_source_condition ' ] = """ + ifnull((select sum( %(second_source_field)s )
from ` tab % ( second_source_dt ) s `
where ` % ( second_join_field ) s ` = " %(detail_id)s "
and ( ` tab % ( second_source_dt ) s ` . docstatus = 1 ) % ( second_source_extra_cond ) s ) , 0 ) """ % a rgs
if args [ ' detail_id ' ] :
if not args . get ( " extra_cond " ) : args [ " extra_cond " ] = " "
2018-09-06 12:57:06 +00:00
2015-08-25 07:19:40 +00:00
frappe . db . sql ( """ update `tab %(target_dt)s `
2016-01-06 11:02:06 +00:00
set % ( target_field ) s = (
( select ifnull ( sum ( % ( source_field ) s ) , 0 )
from ` tab % ( source_dt ) s ` where ` % ( join_field ) s ` = " %(detail_id)s "
and ( docstatus = 1 % ( cond ) s ) % ( extra_cond ) s )
% ( second_source_condition ) s
)
% ( update_modified ) s
2015-08-25 07:19:40 +00:00
where name = ' %(detail_id)s ' """ % a rgs)
2016-01-06 11:02:06 +00:00
def _update_percent_field_in_targets ( self , args , update_modified = True ) :
2015-08-25 07:19:40 +00:00
""" Update percent field in parent transaction """
2016-01-06 11:02:06 +00:00
distinct_transactions = set ( [ d . get ( args [ ' percent_join_field ' ] )
2015-12-30 13:38:11 +00:00
for d in self . get_all_children ( args [ ' source_dt ' ] ) ] )
2015-08-25 07:19:40 +00:00
2015-12-30 13:38:11 +00:00
for name in distinct_transactions :
if name :
args [ ' name ' ] = name
2016-01-06 11:02:06 +00:00
self . _update_percent_field ( args , update_modified )
2015-08-25 07:19:40 +00:00
2016-01-06 11:02:06 +00:00
def _update_percent_field ( self , args , update_modified = True ) :
2015-12-30 13:38:11 +00:00
""" Update percent field in parent transaction """
2016-01-06 11:02:06 +00:00
self . _update_modified ( args , update_modified )
2018-09-06 12:57:06 +00:00
2015-12-30 13:38:11 +00:00
if args . get ( ' target_parent_field ' ) :
frappe . db . sql ( """ update `tab %(target_parent_dt)s `
set % ( target_parent_field ) s = round (
ifnull ( ( select
2019-07-05 11:29:27 +00:00
ifnull ( sum ( if ( abs ( % ( target_ref_field ) s ) > abs ( % ( target_field ) s ) , abs ( % ( target_field ) s ) , abs ( % ( target_ref_field ) s ) ) ) , 0 )
2016-09-12 06:54:46 +00:00
/ sum ( abs ( % ( target_ref_field ) s ) ) * 100
2018-03-27 06:01:44 +00:00
from ` tab % ( target_dt ) s ` where parent = " %(name)s " having sum ( abs ( % ( target_ref_field ) s ) ) > 0 ) , 0 ) , 6 )
2016-01-06 11:02:06 +00:00
% ( update_modified ) s
2015-12-30 13:38:11 +00:00
where name = ' %(name)s ' """ % a rgs)
2016-11-16 05:44:33 +00:00
# update field
if args . get ( ' status_field ' ) :
frappe . db . sql ( """ update `tab %(target_parent_dt)s `
set % ( status_field ) s = if ( % ( target_parent_field ) s < 0.001 ,
2018-03-27 06:01:44 +00:00
' Not %(keyword)s ' , if ( % ( target_parent_field ) s > = 99.999999 ,
2016-11-16 05:44:33 +00:00
' Fully %(keyword)s ' , ' Partly %(keyword)s ' ) )
where name = ' %(name)s ' """ % a rgs)
if update_modified :
target = frappe . get_doc ( args [ " target_parent_dt " ] , args [ " name " ] )
target . set_status ( update = True )
target . notify_update ( )
2014-04-09 13:50:01 +00:00
2016-01-06 11:02:06 +00:00
def _update_modified ( self , args , update_modified ) :
args [ ' update_modified ' ] = ' '
if update_modified :
2018-09-21 04:50:52 +00:00
args [ ' update_modified ' ] = ' , modified = now(), modified_by = {0} ' \
2016-01-06 11:02:06 +00:00
. format ( frappe . db . escape ( frappe . session . user ) )
2014-01-15 12:06:18 +00:00
def update_billing_status_for_zero_amount_refdoc ( self , ref_dt ) :
2019-05-29 08:49:35 +00:00
ref_fieldname = frappe . scrub ( ref_dt )
ref_docs = [ item . get ( ref_fieldname ) for item in ( self . get ( ' items ' ) or [ ] ) if item . get ( ref_fieldname ) ]
if not ref_docs :
return
zero_amount_refdocs = frappe . db . sql_list ( """
SELECT
name
from
` tab { ref_dt } `
where
docstatus = 1
and base_net_total = 0
and name in % ( ref_docs ) s
""" .format(ref_dt=ref_dt), {
' ref_docs ' : ref_docs
} )
if zero_amount_refdocs :
self . update_billing_status ( zero_amount_refdocs , ref_dt , ref_fieldname )
2014-04-09 13:50:01 +00:00
2015-11-04 09:50:50 +00:00
def update_billing_status ( self , zero_amount_refdoc , ref_dt , ref_fieldname ) :
2014-01-15 12:06:18 +00:00
for ref_dn in zero_amount_refdoc :
2015-11-25 10:21:01 +00:00
ref_doc_qty = flt ( frappe . db . sql ( """ select ifnull(sum(qty), 0) from `tab %s Item`
2014-01-15 12:06:18 +00:00
where parent = % s """ % (ref_dt, ' %s ' ), (ref_dn))[0][0])
2014-04-09 13:50:01 +00:00
2015-11-25 10:21:01 +00:00
billed_qty = flt ( frappe . db . sql ( """ select ifnull(sum(qty), 0)
2014-04-09 13:50:01 +00:00
from ` tab % s Item ` where % s = % s and docstatus = 1 """ %
2014-03-28 08:25:00 +00:00
( self . doctype , ref_fieldname , ' %s ' ) , ( ref_dn ) ) [ 0 ] [ 0 ] )
2014-04-09 13:50:01 +00:00
2018-09-06 12:57:06 +00:00
per_billed = ( min ( ref_doc_qty , billed_qty ) / ref_doc_qty ) * 100
2016-07-20 06:38:47 +00:00
ref_doc = frappe . get_doc ( ref_dt , ref_dn )
ref_doc . db_set ( " per_billed " , per_billed )
2016-10-05 17:32:15 +00:00
ref_doc . set_status ( update = True )
2014-04-09 13:50:01 +00:00
2019-07-15 12:32:58 +00:00
def get_allowance_for ( item_code , item_allowance = { } , global_qty_allowance = None , global_amount_allowance = None , qty_or_amount = " qty " ) :
2014-01-03 12:13:19 +00:00
"""
2019-07-15 12:32:58 +00:00
Returns the allowance for the item , if not set , returns global allowance
2014-01-03 12:13:19 +00:00
"""
2019-07-15 12:32:58 +00:00
if qty_or_amount == " qty " :
if item_allowance . get ( item_code , frappe . _dict ( ) ) . get ( " qty " ) :
return item_allowance [ item_code ] . qty , item_allowance , global_qty_allowance , global_amount_allowance
else :
if item_allowance . get ( item_code , frappe . _dict ( ) ) . get ( " amount " ) :
return item_allowance [ item_code ] . amount , item_allowance , global_qty_allowance , global_amount_allowance
qty_allowance , over_billing_allowance = \
frappe . db . get_value ( ' Item ' , item_code , [ ' over_delivery_receipt_allowance ' , ' over_billing_allowance ' ] )
if qty_or_amount == " qty " and not qty_allowance :
if global_qty_allowance == None :
global_qty_allowance = flt ( frappe . db . get_single_value ( ' Stock Settings ' , ' over_delivery_receipt_allowance ' ) )
qty_allowance = global_qty_allowance
elif qty_or_amount == " amount " and not over_billing_allowance :
if global_amount_allowance == None :
global_amount_allowance = flt ( frappe . db . get_single_value ( ' Accounts Settings ' , ' over_billing_allowance ' ) )
over_billing_allowance = global_amount_allowance
if qty_or_amount == " qty " :
allowance = qty_allowance
item_allowance . setdefault ( item_code , frappe . _dict ( ) ) . setdefault ( " qty " , qty_allowance )
else :
allowance = over_billing_allowance
item_allowance . setdefault ( item_code , frappe . _dict ( ) ) . setdefault ( " amount " , over_billing_allowance )
return allowance , item_allowance , global_qty_allowance , global_amount_allowance