blob: 0992345537852f5789479ab40bc2f1a8ffad3076 [file] [log] [blame]
Rohit Waghchauref704eb72023-03-30 11:32:39 +05301from collections import defaultdict
2
Rohit Waghchauree6143ab2023-03-13 14:51:43 +05303import frappe
4from frappe.query_builder.functions import CombineDatetime, Sum
5from frappe.utils import flt
Rohit Waghchaurec1132d12023-03-24 10:14:09 +05306from frappe.utils.deprecations import deprecated
Rohit Waghchauree6143ab2023-03-13 14:51:43 +05307
8
9class DeprecatedSerialNoValuation:
Rohit Waghchaurec1132d12023-03-24 10:14:09 +053010 @deprecated
Rohit Waghchauree6143ab2023-03-13 14:51:43 +053011 def calculate_stock_value_from_deprecarated_ledgers(self):
12 serial_nos = list(
13 filter(lambda x: x not in self.serial_no_incoming_rate and x, self.get_serial_nos())
14 )
15
16 actual_qty = flt(self.sle.actual_qty)
17
18 stock_value_change = 0
19 if actual_qty < 0:
20 # In case of delivery/stock issue, get average purchase rate
21 # of serial nos of current entry
22 if not self.sle.is_cancelled:
23 outgoing_value = self.get_incoming_value_for_serial_nos(serial_nos)
24 stock_value_change = -1 * outgoing_value
25 else:
26 stock_value_change = actual_qty * self.sle.outgoing_rate
27
28 self.stock_value_change += stock_value_change
29
Rohit Waghchaurec1132d12023-03-24 10:14:09 +053030 @deprecated
Rohit Waghchauree6143ab2023-03-13 14:51:43 +053031 def get_incoming_value_for_serial_nos(self, serial_nos):
32 # get rate from serial nos within same company
33 all_serial_nos = frappe.get_all(
34 "Serial No", fields=["purchase_rate", "name", "company"], filters={"name": ("in", serial_nos)}
35 )
36
37 incoming_values = 0.0
38 for d in all_serial_nos:
39 if d.company == self.sle.company:
40 self.serial_no_incoming_rate[d.name] = flt(d.purchase_rate)
41 incoming_values += flt(d.purchase_rate)
42
43 # Get rate for serial nos which has been transferred to other company
44 invalid_serial_nos = [d.name for d in all_serial_nos if d.company != self.sle.company]
45 for serial_no in invalid_serial_nos:
46 incoming_rate = frappe.db.sql(
47 """
48 select incoming_rate
49 from `tabStock Ledger Entry`
50 where
51 company = %s
52 and actual_qty > 0
53 and is_cancelled = 0
54 and (serial_no = %s
55 or serial_no like %s
56 or serial_no like %s
57 or serial_no like %s
58 )
59 order by posting_date desc
60 limit 1
61 """,
62 (self.sle.company, serial_no, serial_no + "\n%", "%\n" + serial_no, "%\n" + serial_no + "\n%"),
63 )
64
65 self.serial_no_incoming_rate[serial_no] = flt(incoming_rate[0][0]) if incoming_rate else 0
66 incoming_values += self.serial_no_incoming_rate[serial_no]
67
68 return incoming_values
69
70
71class DeprecatedBatchNoValuation:
Rohit Waghchaurec1132d12023-03-24 10:14:09 +053072 @deprecated
Rohit Waghchauree6143ab2023-03-13 14:51:43 +053073 def calculate_avg_rate_from_deprecarated_ledgers(self):
Rohit Waghchaure5bb31732023-03-21 10:54:41 +053074 entries = self.get_sle_for_batches()
75 for ledger in entries:
Rohit Waghchauref704eb72023-03-30 11:32:39 +053076 self.stock_value_differece[ledger.batch_no] += flt(ledger.batch_value)
Rohit Waghchaure86da3062023-03-20 14:15:34 +053077 self.available_qty[ledger.batch_no] += flt(ledger.batch_qty)
Rohit Waghchauree6143ab2023-03-13 14:51:43 +053078
Rohit Waghchaurec1132d12023-03-24 10:14:09 +053079 @deprecated
Rohit Waghchauree6143ab2023-03-13 14:51:43 +053080 def get_sle_for_batches(self):
Rohit Waghchauref704eb72023-03-30 11:32:39 +053081 if not self.batchwise_valuation_batches:
82 return []
83
Rohit Waghchauree6143ab2023-03-13 14:51:43 +053084 sle = frappe.qb.DocType("Stock Ledger Entry")
85
86 timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime(
87 self.sle.posting_date, self.sle.posting_time
88 )
89 if self.sle.creation:
90 timestamp_condition |= (
91 CombineDatetime(sle.posting_date, sle.posting_time)
92 == CombineDatetime(self.sle.posting_date, self.sle.posting_time)
93 ) & (sle.creation < self.sle.creation)
94
Rohit Waghchaurec2d74612023-03-29 11:40:36 +053095 query = (
Rohit Waghchauree6143ab2023-03-13 14:51:43 +053096 frappe.qb.from_(sle)
97 .select(
98 sle.batch_no,
99 Sum(sle.stock_value_difference).as_("batch_value"),
100 Sum(sle.actual_qty).as_("batch_qty"),
101 )
102 .where(
103 (sle.item_code == self.sle.item_code)
Rohit Waghchauree6143ab2023-03-13 14:51:43 +0530104 & (sle.warehouse == self.sle.warehouse)
Rohit Waghchauref704eb72023-03-30 11:32:39 +0530105 & (sle.batch_no.isin(self.batchwise_valuation_batches))
106 & (sle.batch_no.isnotnull())
Rohit Waghchauree6143ab2023-03-13 14:51:43 +0530107 & (sle.is_cancelled == 0)
108 )
109 .where(timestamp_condition)
110 .groupby(sle.batch_no)
Rohit Waghchaurec2d74612023-03-29 11:40:36 +0530111 )
112
113 if self.sle.name:
114 query = query.where(sle.name != self.sle.name)
115
116 return query.run(as_dict=True)
Rohit Waghchauref704eb72023-03-30 11:32:39 +0530117
118 @deprecated
119 def calculate_avg_rate_for_non_batchwise_valuation(self):
120 if not self.non_batchwise_valuation_batches:
121 return
122
123 avg_rate = self.get_avg_rate_for_non_batchwise_valuation_batches()
124 avilable_qty = self.get_available_qty_for_non_batchwise_valuation_batches()
125
126 for batch_no in self.non_batchwise_valuation_batches:
127 self.stock_value_differece[batch_no] = avg_rate
128 self.available_qty[batch_no] = avilable_qty.get(batch_no, 0)
129
130 @deprecated
131 def get_avg_rate_for_non_batchwise_valuation_batches(self):
132 stock_value, qty = self.get_balance_value_and_qty_from_sl_entries()
133 stock_value, qty = self.get_balance_value_and_qty_from_bundle(stock_value, qty)
134
135 return stock_value / qty if qty else 0
136
137 @deprecated
138 def get_balance_value_and_qty_from_sl_entries(self):
139 stock_value_difference = 0.0
140 available_qty = 0.0
141
142 sle = frappe.qb.DocType("Stock Ledger Entry")
143 batch = frappe.qb.DocType("Batch")
144
145 timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime(
146 self.sle.posting_date, self.sle.posting_time
147 )
148 if self.sle.creation:
149 timestamp_condition |= (
150 CombineDatetime(sle.posting_date, sle.posting_time)
151 == CombineDatetime(self.sle.posting_date, self.sle.posting_time)
152 ) & (sle.creation < self.sle.creation)
153
154 query = (
155 frappe.qb.from_(sle)
156 .inner_join(batch)
157 .on(sle.batch_no == batch.name)
158 .select(
159 Sum(sle.stock_value_difference).as_("batch_value"),
160 Sum(sle.actual_qty).as_("batch_qty"),
161 )
162 .where(
163 (sle.item_code == self.sle.item_code)
164 & (sle.warehouse == self.sle.warehouse)
165 & (sle.batch_no.isnotnull())
166 & (batch.use_batchwise_valuation == 0)
167 & (sle.is_cancelled == 0)
168 )
169 .where(timestamp_condition)
170 )
171
172 if self.sle.name:
173 query = query.where(sle.name != self.sle.name)
174
175 for d in query.run(as_dict=True):
176 stock_value_difference += flt(d.batch_value)
177 available_qty += flt(d.batch_qty)
178
179 return stock_value_difference, available_qty
180
181 @deprecated
182 def get_balance_value_and_qty_from_bundle(self, stock_value, qty):
183 bundle = frappe.qb.DocType("Serial and Batch Bundle")
184 bundle_child = frappe.qb.DocType("Serial and Batch Entry")
185 batch = frappe.qb.DocType("Batch")
186
187 timestamp_condition = CombineDatetime(
188 bundle.posting_date, bundle.posting_time
189 ) < CombineDatetime(self.sle.posting_date, self.sle.posting_time)
190
191 if self.sle.creation:
192 timestamp_condition |= (
193 CombineDatetime(bundle.posting_date, bundle.posting_time)
194 == CombineDatetime(self.sle.posting_date, self.sle.posting_time)
195 ) & (bundle.creation < self.sle.creation)
196
197 query = (
198 frappe.qb.from_(bundle)
199 .inner_join(bundle_child)
200 .on(bundle.name == bundle_child.parent)
201 .inner_join(batch)
202 .on(bundle_child.batch_no == batch.name)
203 .select(
204 Sum(bundle_child.stock_value_difference).as_("batch_value"),
205 Sum(bundle_child.qty).as_("batch_qty"),
206 )
207 .where(
208 (bundle.item_code == self.sle.item_code)
209 & (bundle.warehouse == self.sle.warehouse)
210 & (bundle_child.batch_no.isnotnull())
211 & (batch.use_batchwise_valuation == 0)
212 & (bundle.is_cancelled == 0)
213 )
214 .where(timestamp_condition)
215 )
216
217 if self.sle.serial_and_batch_bundle:
218 query = query.where(bundle.name != self.sle.serial_and_batch_bundle)
219
220 for d in query.run(as_dict=True):
221 stock_value += flt(d.batch_value)
222 qty += flt(d.batch_qty)
223
224 return stock_value, qty
225
226 @deprecated
227 def get_available_qty_for_non_batchwise_valuation_batches(self):
228 available_qty = defaultdict(float)
229 self.set_available_qty_for_non_batchwise_valuation_batches_from_sle(available_qty)
230 self.set_available_qty_for_non_batchwise_valuation_batches_from_bundle(available_qty)
231
232 return available_qty
233
234 @deprecated
235 def set_available_qty_for_non_batchwise_valuation_batches_from_sle(self, available_qty):
236 sle = frappe.qb.DocType("Stock Ledger Entry")
237
238 timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime(
239 self.sle.posting_date, self.sle.posting_time
240 )
241 if self.sle.creation:
242 timestamp_condition |= (
243 CombineDatetime(sle.posting_date, sle.posting_time)
244 == CombineDatetime(self.sle.posting_date, self.sle.posting_time)
245 ) & (sle.creation < self.sle.creation)
246
247 query = (
248 frappe.qb.from_(sle)
249 .select(
250 sle.batch_no,
251 Sum(sle.actual_qty).as_("batch_qty"),
252 )
253 .where(
254 (sle.item_code == self.sle.item_code)
255 & (sle.warehouse == self.sle.warehouse)
256 & (sle.batch_no.isin(self.non_batchwise_valuation_batches))
257 & (sle.is_cancelled == 0)
258 )
259 .where(timestamp_condition)
260 .groupby(sle.batch_no)
261 )
262
263 if self.sle.name:
264 query = query.where(sle.name != self.sle.name)
265
266 for d in query.run(as_dict=True):
267 available_qty[d.batch_no] += flt(d.batch_qty)
268
269 @deprecated
270 def set_available_qty_for_non_batchwise_valuation_batches_from_bundle(self, available_qty):
271 bundle = frappe.qb.DocType("Serial and Batch Bundle")
272 bundle_child = frappe.qb.DocType("Serial and Batch Entry")
273
274 timestamp_condition = CombineDatetime(
275 bundle.posting_date, bundle.posting_time
276 ) < CombineDatetime(self.sle.posting_date, self.sle.posting_time)
277
278 if self.sle.creation:
279 timestamp_condition |= (
280 CombineDatetime(bundle.posting_date, bundle.posting_time)
281 == CombineDatetime(self.sle.posting_date, self.sle.posting_time)
282 ) & (bundle.creation < self.sle.creation)
283
284 query = (
285 frappe.qb.from_(bundle)
286 .inner_join(bundle_child)
287 .on(bundle.name == bundle_child.parent)
288 .select(
289 bundle_child.batch_no,
290 Sum(bundle_child.qty).as_("batch_qty"),
291 )
292 .where(
293 (bundle.item_code == self.sle.item_code)
294 & (bundle.warehouse == self.sle.warehouse)
295 & (bundle_child.batch_no.isin(self.non_batchwise_valuation_batches))
296 & (bundle.is_cancelled == 0)
297 )
298 .where(timestamp_condition)
299 .groupby(bundle_child.batch_no)
300 )
301
302 if self.sle.serial_and_batch_bundle:
303 query = query.where(bundle.name != self.sle.serial_and_batch_bundle)
304
305 for d in query.run(as_dict=True):
306 available_qty[d.batch_no] += flt(d.batch_qty)