blob: 1e1d8fdeca9ea10232fb2379d19603d5507936f8 [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:
Rohit Waghchaured3ceb072023-03-31 09:03:54 +053040 self.serial_no_incoming_rate[d.name] += flt(d.purchase_rate)
Rohit Waghchauree6143ab2023-03-13 14:51:43 +053041 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
Rohit Waghchaured3ceb072023-03-31 09:03:54 +053052 and serial_and_batch_bundle IS NULL
Rohit Waghchauree6143ab2023-03-13 14:51:43 +053053 and actual_qty > 0
54 and is_cancelled = 0
55 and (serial_no = %s
56 or serial_no like %s
57 or serial_no like %s
58 or serial_no like %s
59 )
60 order by posting_date desc
61 limit 1
62 """,
63 (self.sle.company, serial_no, serial_no + "\n%", "%\n" + serial_no, "%\n" + serial_no + "\n%"),
64 )
65
Rohit Waghchaured3ceb072023-03-31 09:03:54 +053066 self.serial_no_incoming_rate[serial_no] += flt(incoming_rate[0][0]) if incoming_rate else 0
Rohit Waghchauree6143ab2023-03-13 14:51:43 +053067 incoming_values += self.serial_no_incoming_rate[serial_no]
68
69 return incoming_values
70
71
72class DeprecatedBatchNoValuation:
Rohit Waghchaurec1132d12023-03-24 10:14:09 +053073 @deprecated
Rohit Waghchauree6143ab2023-03-13 14:51:43 +053074 def calculate_avg_rate_from_deprecarated_ledgers(self):
Rohit Waghchaure5bb31732023-03-21 10:54:41 +053075 entries = self.get_sle_for_batches()
76 for ledger in entries:
Rohit Waghchauref704eb72023-03-30 11:32:39 +053077 self.stock_value_differece[ledger.batch_no] += flt(ledger.batch_value)
Rohit Waghchaure86da3062023-03-20 14:15:34 +053078 self.available_qty[ledger.batch_no] += flt(ledger.batch_qty)
Rohit Waghchauree6143ab2023-03-13 14:51:43 +053079
Rohit Waghchaurec1132d12023-03-24 10:14:09 +053080 @deprecated
Rohit Waghchauree6143ab2023-03-13 14:51:43 +053081 def get_sle_for_batches(self):
Rohit Waghchauref704eb72023-03-30 11:32:39 +053082 if not self.batchwise_valuation_batches:
83 return []
84
Rohit Waghchauree6143ab2023-03-13 14:51:43 +053085 sle = frappe.qb.DocType("Stock Ledger Entry")
86
87 timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime(
88 self.sle.posting_date, self.sle.posting_time
89 )
90 if self.sle.creation:
91 timestamp_condition |= (
92 CombineDatetime(sle.posting_date, sle.posting_time)
93 == CombineDatetime(self.sle.posting_date, self.sle.posting_time)
94 ) & (sle.creation < self.sle.creation)
95
Rohit Waghchaurec2d74612023-03-29 11:40:36 +053096 query = (
Rohit Waghchauree6143ab2023-03-13 14:51:43 +053097 frappe.qb.from_(sle)
98 .select(
99 sle.batch_no,
100 Sum(sle.stock_value_difference).as_("batch_value"),
101 Sum(sle.actual_qty).as_("batch_qty"),
102 )
103 .where(
104 (sle.item_code == self.sle.item_code)
Rohit Waghchauree6143ab2023-03-13 14:51:43 +0530105 & (sle.warehouse == self.sle.warehouse)
Rohit Waghchauref704eb72023-03-30 11:32:39 +0530106 & (sle.batch_no.isin(self.batchwise_valuation_batches))
107 & (sle.batch_no.isnotnull())
Rohit Waghchauree6143ab2023-03-13 14:51:43 +0530108 & (sle.is_cancelled == 0)
109 )
110 .where(timestamp_condition)
111 .groupby(sle.batch_no)
Rohit Waghchaurec2d74612023-03-29 11:40:36 +0530112 )
113
114 if self.sle.name:
115 query = query.where(sle.name != self.sle.name)
116
117 return query.run(as_dict=True)
Rohit Waghchauref704eb72023-03-30 11:32:39 +0530118
119 @deprecated
120 def calculate_avg_rate_for_non_batchwise_valuation(self):
121 if not self.non_batchwise_valuation_batches:
122 return
123
124 avg_rate = self.get_avg_rate_for_non_batchwise_valuation_batches()
125 avilable_qty = self.get_available_qty_for_non_batchwise_valuation_batches()
126
127 for batch_no in self.non_batchwise_valuation_batches:
128 self.stock_value_differece[batch_no] = avg_rate
129 self.available_qty[batch_no] = avilable_qty.get(batch_no, 0)
130
131 @deprecated
132 def get_avg_rate_for_non_batchwise_valuation_batches(self):
133 stock_value, qty = self.get_balance_value_and_qty_from_sl_entries()
134 stock_value, qty = self.get_balance_value_and_qty_from_bundle(stock_value, qty)
135
136 return stock_value / qty if qty else 0
137
138 @deprecated
139 def get_balance_value_and_qty_from_sl_entries(self):
140 stock_value_difference = 0.0
141 available_qty = 0.0
142
143 sle = frappe.qb.DocType("Stock Ledger Entry")
144 batch = frappe.qb.DocType("Batch")
145
146 timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime(
147 self.sle.posting_date, self.sle.posting_time
148 )
149 if self.sle.creation:
150 timestamp_condition |= (
151 CombineDatetime(sle.posting_date, sle.posting_time)
152 == CombineDatetime(self.sle.posting_date, self.sle.posting_time)
153 ) & (sle.creation < self.sle.creation)
154
155 query = (
156 frappe.qb.from_(sle)
157 .inner_join(batch)
158 .on(sle.batch_no == batch.name)
159 .select(
160 Sum(sle.stock_value_difference).as_("batch_value"),
161 Sum(sle.actual_qty).as_("batch_qty"),
162 )
163 .where(
164 (sle.item_code == self.sle.item_code)
165 & (sle.warehouse == self.sle.warehouse)
166 & (sle.batch_no.isnotnull())
167 & (batch.use_batchwise_valuation == 0)
168 & (sle.is_cancelled == 0)
169 )
170 .where(timestamp_condition)
171 )
172
173 if self.sle.name:
174 query = query.where(sle.name != self.sle.name)
175
176 for d in query.run(as_dict=True):
177 stock_value_difference += flt(d.batch_value)
178 available_qty += flt(d.batch_qty)
179
180 return stock_value_difference, available_qty
181
182 @deprecated
183 def get_balance_value_and_qty_from_bundle(self, stock_value, qty):
184 bundle = frappe.qb.DocType("Serial and Batch Bundle")
185 bundle_child = frappe.qb.DocType("Serial and Batch Entry")
186 batch = frappe.qb.DocType("Batch")
187
188 timestamp_condition = CombineDatetime(
189 bundle.posting_date, bundle.posting_time
190 ) < CombineDatetime(self.sle.posting_date, self.sle.posting_time)
191
192 if self.sle.creation:
193 timestamp_condition |= (
194 CombineDatetime(bundle.posting_date, bundle.posting_time)
195 == CombineDatetime(self.sle.posting_date, self.sle.posting_time)
196 ) & (bundle.creation < self.sle.creation)
197
198 query = (
199 frappe.qb.from_(bundle)
200 .inner_join(bundle_child)
201 .on(bundle.name == bundle_child.parent)
202 .inner_join(batch)
203 .on(bundle_child.batch_no == batch.name)
204 .select(
205 Sum(bundle_child.stock_value_difference).as_("batch_value"),
206 Sum(bundle_child.qty).as_("batch_qty"),
207 )
208 .where(
209 (bundle.item_code == self.sle.item_code)
210 & (bundle.warehouse == self.sle.warehouse)
211 & (bundle_child.batch_no.isnotnull())
212 & (batch.use_batchwise_valuation == 0)
213 & (bundle.is_cancelled == 0)
Rohit Waghchauref8bf4aa2023-04-02 13:13:42 +0530214 & (bundle.type_of_transaction.isin(["Inward", "Outward"]))
Rohit Waghchauref704eb72023-03-30 11:32:39 +0530215 )
216 .where(timestamp_condition)
217 )
218
219 if self.sle.serial_and_batch_bundle:
220 query = query.where(bundle.name != self.sle.serial_and_batch_bundle)
221
222 for d in query.run(as_dict=True):
223 stock_value += flt(d.batch_value)
224 qty += flt(d.batch_qty)
225
226 return stock_value, qty
227
228 @deprecated
229 def get_available_qty_for_non_batchwise_valuation_batches(self):
230 available_qty = defaultdict(float)
231 self.set_available_qty_for_non_batchwise_valuation_batches_from_sle(available_qty)
232 self.set_available_qty_for_non_batchwise_valuation_batches_from_bundle(available_qty)
233
234 return available_qty
235
236 @deprecated
237 def set_available_qty_for_non_batchwise_valuation_batches_from_sle(self, available_qty):
238 sle = frappe.qb.DocType("Stock Ledger Entry")
239
240 timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime(
241 self.sle.posting_date, self.sle.posting_time
242 )
243 if self.sle.creation:
244 timestamp_condition |= (
245 CombineDatetime(sle.posting_date, sle.posting_time)
246 == CombineDatetime(self.sle.posting_date, self.sle.posting_time)
247 ) & (sle.creation < self.sle.creation)
248
249 query = (
250 frappe.qb.from_(sle)
251 .select(
252 sle.batch_no,
253 Sum(sle.actual_qty).as_("batch_qty"),
254 )
255 .where(
256 (sle.item_code == self.sle.item_code)
257 & (sle.warehouse == self.sle.warehouse)
258 & (sle.batch_no.isin(self.non_batchwise_valuation_batches))
259 & (sle.is_cancelled == 0)
260 )
261 .where(timestamp_condition)
262 .groupby(sle.batch_no)
263 )
264
265 if self.sle.name:
266 query = query.where(sle.name != self.sle.name)
267
268 for d in query.run(as_dict=True):
269 available_qty[d.batch_no] += flt(d.batch_qty)
270
271 @deprecated
272 def set_available_qty_for_non_batchwise_valuation_batches_from_bundle(self, available_qty):
273 bundle = frappe.qb.DocType("Serial and Batch Bundle")
274 bundle_child = frappe.qb.DocType("Serial and Batch Entry")
275
276 timestamp_condition = CombineDatetime(
277 bundle.posting_date, bundle.posting_time
278 ) < CombineDatetime(self.sle.posting_date, self.sle.posting_time)
279
280 if self.sle.creation:
281 timestamp_condition |= (
282 CombineDatetime(bundle.posting_date, bundle.posting_time)
283 == CombineDatetime(self.sle.posting_date, self.sle.posting_time)
284 ) & (bundle.creation < self.sle.creation)
285
286 query = (
287 frappe.qb.from_(bundle)
288 .inner_join(bundle_child)
289 .on(bundle.name == bundle_child.parent)
290 .select(
291 bundle_child.batch_no,
292 Sum(bundle_child.qty).as_("batch_qty"),
293 )
294 .where(
295 (bundle.item_code == self.sle.item_code)
296 & (bundle.warehouse == self.sle.warehouse)
297 & (bundle_child.batch_no.isin(self.non_batchwise_valuation_batches))
298 & (bundle.is_cancelled == 0)
299 )
300 .where(timestamp_condition)
301 .groupby(bundle_child.batch_no)
302 )
303
304 if self.sle.serial_and_batch_bundle:
305 query = query.where(bundle.name != self.sle.serial_and_batch_bundle)
306
307 for d in query.run(as_dict=True):
308 available_qty[d.batch_no] += flt(d.batch_qty)