Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 1 | import frappe |
| 2 | from frappe.query_builder.functions import CombineDatetime, Sum |
| 3 | from frappe.utils import flt |
Rohit Waghchaure | c1132d1 | 2023-03-24 10:14:09 +0530 | [diff] [blame] | 4 | from frappe.utils.deprecations import deprecated |
Rohit Waghchaure | f4cfc58 | 2023-04-11 13:22:15 +0530 | [diff] [blame] | 5 | from pypika import Order |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 6 | |
| 7 | |
| 8 | class DeprecatedSerialNoValuation: |
Rohit Waghchaure | c1132d1 | 2023-03-24 10:14:09 +0530 | [diff] [blame] | 9 | @deprecated |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 10 | def calculate_stock_value_from_deprecarated_ledgers(self): |
rohitwaghchaure | a5232d9 | 2024-03-01 22:26:54 +0530 | [diff] [blame] | 11 | if not frappe.db.get_value( |
| 12 | "Stock Ledger Entry", {"serial_no": ("is", "set"), "is_cancelled": 0}, "name" |
| 13 | ): |
| 14 | return |
| 15 | |
rohitwaghchaure | 6379238 | 2024-03-04 12:04:41 +0530 | [diff] [blame] | 16 | serial_nos = self.get_filterd_serial_nos() |
| 17 | if not serial_nos: |
| 18 | return |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 19 | |
| 20 | actual_qty = flt(self.sle.actual_qty) |
| 21 | |
| 22 | stock_value_change = 0 |
| 23 | if actual_qty < 0: |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 24 | if not self.sle.is_cancelled: |
| 25 | outgoing_value = self.get_incoming_value_for_serial_nos(serial_nos) |
| 26 | stock_value_change = -1 * outgoing_value |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 27 | |
| 28 | self.stock_value_change += stock_value_change |
| 29 | |
rohitwaghchaure | 6379238 | 2024-03-04 12:04:41 +0530 | [diff] [blame] | 30 | def get_filterd_serial_nos(self): |
| 31 | serial_nos = [] |
| 32 | non_filtered_serial_nos = self.get_serial_nos() |
| 33 | |
| 34 | # If the serial no inwarded using the Serial and Batch Bundle, then the serial no should not be considered |
| 35 | for serial_no in non_filtered_serial_nos: |
| 36 | if serial_no and serial_no not in self.serial_no_incoming_rate: |
| 37 | serial_nos.append(serial_no) |
| 38 | |
| 39 | return serial_nos |
| 40 | |
Rohit Waghchaure | c1132d1 | 2023-03-24 10:14:09 +0530 | [diff] [blame] | 41 | @deprecated |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 42 | def get_incoming_value_for_serial_nos(self, serial_nos): |
rohitwaghchaure | 6379238 | 2024-03-04 12:04:41 +0530 | [diff] [blame] | 43 | from erpnext.stock.utils import get_combine_datetime |
| 44 | |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 45 | # get rate from serial nos within same company |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 46 | incoming_values = 0.0 |
rohitwaghchaure | a5232d9 | 2024-03-01 22:26:54 +0530 | [diff] [blame] | 47 | for serial_no in serial_nos: |
Rohit Waghchaure | f4cfc58 | 2023-04-11 13:22:15 +0530 | [diff] [blame] | 48 | table = frappe.qb.DocType("Stock Ledger Entry") |
rohitwaghchaure | a5232d9 | 2024-03-01 22:26:54 +0530 | [diff] [blame] | 49 | stock_ledgers = ( |
Rohit Waghchaure | f4cfc58 | 2023-04-11 13:22:15 +0530 | [diff] [blame] | 50 | frappe.qb.from_(table) |
rohitwaghchaure | a5232d9 | 2024-03-01 22:26:54 +0530 | [diff] [blame] | 51 | .select(table.incoming_rate, table.actual_qty, table.stock_value_difference) |
Rohit Waghchaure | f4cfc58 | 2023-04-11 13:22:15 +0530 | [diff] [blame] | 52 | .where( |
| 53 | ( |
| 54 | (table.serial_no == serial_no) |
| 55 | | (table.serial_no.like(serial_no + "\n%")) |
| 56 | | (table.serial_no.like("%\n" + serial_no)) |
| 57 | | (table.serial_no.like("%\n" + serial_no + "\n%")) |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 58 | ) |
Rohit Waghchaure | f4cfc58 | 2023-04-11 13:22:15 +0530 | [diff] [blame] | 59 | & (table.company == self.sle.company) |
rohitwaghchaure | 6379238 | 2024-03-04 12:04:41 +0530 | [diff] [blame] | 60 | & (table.warehouse == self.sle.warehouse) |
Rohit Waghchaure | f4cfc58 | 2023-04-11 13:22:15 +0530 | [diff] [blame] | 61 | & (table.serial_and_batch_bundle.isnull()) |
rohitwaghchaure | 6379238 | 2024-03-04 12:04:41 +0530 | [diff] [blame] | 62 | & (table.actual_qty > 0) |
Rohit Waghchaure | f4cfc58 | 2023-04-11 13:22:15 +0530 | [diff] [blame] | 63 | & (table.is_cancelled == 0) |
rohitwaghchaure | 6379238 | 2024-03-04 12:04:41 +0530 | [diff] [blame] | 64 | & table.posting_datetime |
| 65 | <= get_combine_datetime(self.sle.posting_date, self.sle.posting_time) |
Rohit Waghchaure | f4cfc58 | 2023-04-11 13:22:15 +0530 | [diff] [blame] | 66 | ) |
rohitwaghchaure | a5232d9 | 2024-03-01 22:26:54 +0530 | [diff] [blame] | 67 | .orderby(table.posting_datetime, order=Order.desc) |
rohitwaghchaure | 6379238 | 2024-03-04 12:04:41 +0530 | [diff] [blame] | 68 | .limit(1) |
rohitwaghchaure | a5232d9 | 2024-03-01 22:26:54 +0530 | [diff] [blame] | 69 | ).run(as_dict=1) |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 70 | |
rohitwaghchaure | a5232d9 | 2024-03-01 22:26:54 +0530 | [diff] [blame] | 71 | for sle in stock_ledgers: |
rohitwaghchaure | 6379238 | 2024-03-04 12:04:41 +0530 | [diff] [blame] | 72 | self.serial_no_incoming_rate[serial_no] += flt(sle.incoming_rate) |
rohitwaghchaure | a5232d9 | 2024-03-01 22:26:54 +0530 | [diff] [blame] | 73 | incoming_values += self.serial_no_incoming_rate[serial_no] |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 74 | |
| 75 | return incoming_values |
| 76 | |
| 77 | |
| 78 | class DeprecatedBatchNoValuation: |
Rohit Waghchaure | c1132d1 | 2023-03-24 10:14:09 +0530 | [diff] [blame] | 79 | @deprecated |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 80 | def calculate_avg_rate_from_deprecarated_ledgers(self): |
Rohit Waghchaure | 5bb3173 | 2023-03-21 10:54:41 +0530 | [diff] [blame] | 81 | entries = self.get_sle_for_batches() |
| 82 | for ledger in entries: |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 83 | self.stock_value_differece[ledger.batch_no] += flt(ledger.batch_value) |
Rohit Waghchaure | 86da306 | 2023-03-20 14:15:34 +0530 | [diff] [blame] | 84 | self.available_qty[ledger.batch_no] += flt(ledger.batch_qty) |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 85 | |
Rohit Waghchaure | c1132d1 | 2023-03-24 10:14:09 +0530 | [diff] [blame] | 86 | @deprecated |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 87 | def get_sle_for_batches(self): |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 88 | if not self.batchwise_valuation_batches: |
| 89 | return [] |
| 90 | |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 91 | sle = frappe.qb.DocType("Stock Ledger Entry") |
| 92 | |
| 93 | timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime( |
| 94 | self.sle.posting_date, self.sle.posting_time |
| 95 | ) |
| 96 | if self.sle.creation: |
| 97 | timestamp_condition |= ( |
| 98 | CombineDatetime(sle.posting_date, sle.posting_time) |
| 99 | == CombineDatetime(self.sle.posting_date, self.sle.posting_time) |
| 100 | ) & (sle.creation < self.sle.creation) |
| 101 | |
Rohit Waghchaure | c2d7461 | 2023-03-29 11:40:36 +0530 | [diff] [blame] | 102 | query = ( |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 103 | frappe.qb.from_(sle) |
| 104 | .select( |
| 105 | sle.batch_no, |
| 106 | Sum(sle.stock_value_difference).as_("batch_value"), |
| 107 | Sum(sle.actual_qty).as_("batch_qty"), |
| 108 | ) |
| 109 | .where( |
| 110 | (sle.item_code == self.sle.item_code) |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 111 | & (sle.warehouse == self.sle.warehouse) |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 112 | & (sle.batch_no.isin(self.batchwise_valuation_batches)) |
| 113 | & (sle.batch_no.isnotnull()) |
Rohit Waghchaure | e6143ab | 2023-03-13 14:51:43 +0530 | [diff] [blame] | 114 | & (sle.is_cancelled == 0) |
| 115 | ) |
| 116 | .where(timestamp_condition) |
| 117 | .groupby(sle.batch_no) |
Rohit Waghchaure | c2d7461 | 2023-03-29 11:40:36 +0530 | [diff] [blame] | 118 | ) |
| 119 | |
| 120 | if self.sle.name: |
| 121 | query = query.where(sle.name != self.sle.name) |
| 122 | |
| 123 | return query.run(as_dict=True) |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 124 | |
| 125 | @deprecated |
| 126 | def calculate_avg_rate_for_non_batchwise_valuation(self): |
| 127 | if not self.non_batchwise_valuation_batches: |
| 128 | return |
| 129 | |
Rohit Waghchaure | 40ab3bd | 2023-06-01 16:08:49 +0530 | [diff] [blame] | 130 | self.non_batchwise_balance_value = 0.0 |
| 131 | self.non_batchwise_balance_qty = 0.0 |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 132 | |
Rohit Waghchaure | 40ab3bd | 2023-06-01 16:08:49 +0530 | [diff] [blame] | 133 | self.set_balance_value_for_non_batchwise_valuation_batches() |
| 134 | |
| 135 | for batch_no, ledger in self.batch_nos.items(): |
| 136 | if batch_no not in self.non_batchwise_valuation_batches: |
| 137 | continue |
| 138 | |
Rohit Waghchaure | f968f0f | 2023-06-14 23:22:22 +0530 | [diff] [blame] | 139 | if not self.non_batchwise_balance_qty: |
| 140 | continue |
| 141 | |
Akhil Narang | 3effaf2 | 2024-03-27 11:37:26 +0530 | [diff] [blame] | 142 | self.batch_avg_rate[batch_no] = self.non_batchwise_balance_value / self.non_batchwise_balance_qty |
Rohit Waghchaure | 40ab3bd | 2023-06-01 16:08:49 +0530 | [diff] [blame] | 143 | |
| 144 | stock_value_change = self.batch_avg_rate[batch_no] * ledger.qty |
| 145 | self.stock_value_change += stock_value_change |
| 146 | |
| 147 | frappe.db.set_value( |
| 148 | "Serial and Batch Entry", |
| 149 | ledger.name, |
| 150 | { |
| 151 | "stock_value_difference": stock_value_change, |
| 152 | "incoming_rate": self.batch_avg_rate[batch_no], |
| 153 | }, |
| 154 | ) |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 155 | |
| 156 | @deprecated |
Rohit Waghchaure | 40ab3bd | 2023-06-01 16:08:49 +0530 | [diff] [blame] | 157 | def set_balance_value_for_non_batchwise_valuation_batches(self): |
| 158 | self.set_balance_value_from_sl_entries() |
| 159 | self.set_balance_value_from_bundle() |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 160 | |
| 161 | @deprecated |
Rohit Waghchaure | 40ab3bd | 2023-06-01 16:08:49 +0530 | [diff] [blame] | 162 | def set_balance_value_from_sl_entries(self) -> None: |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 163 | sle = frappe.qb.DocType("Stock Ledger Entry") |
| 164 | batch = frappe.qb.DocType("Batch") |
| 165 | |
| 166 | timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime( |
| 167 | self.sle.posting_date, self.sle.posting_time |
| 168 | ) |
| 169 | if self.sle.creation: |
| 170 | timestamp_condition |= ( |
| 171 | CombineDatetime(sle.posting_date, sle.posting_time) |
| 172 | == CombineDatetime(self.sle.posting_date, self.sle.posting_time) |
| 173 | ) & (sle.creation < self.sle.creation) |
| 174 | |
| 175 | query = ( |
| 176 | frappe.qb.from_(sle) |
| 177 | .inner_join(batch) |
| 178 | .on(sle.batch_no == batch.name) |
| 179 | .select( |
Rohit Waghchaure | 40ab3bd | 2023-06-01 16:08:49 +0530 | [diff] [blame] | 180 | sle.batch_no, |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 181 | Sum(sle.actual_qty).as_("batch_qty"), |
Rohit Waghchaure | 40ab3bd | 2023-06-01 16:08:49 +0530 | [diff] [blame] | 182 | Sum(sle.stock_value_difference).as_("batch_value"), |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 183 | ) |
| 184 | .where( |
| 185 | (sle.item_code == self.sle.item_code) |
| 186 | & (sle.warehouse == self.sle.warehouse) |
| 187 | & (sle.batch_no.isnotnull()) |
| 188 | & (batch.use_batchwise_valuation == 0) |
| 189 | & (sle.is_cancelled == 0) |
| 190 | ) |
| 191 | .where(timestamp_condition) |
Rohit Waghchaure | 40ab3bd | 2023-06-01 16:08:49 +0530 | [diff] [blame] | 192 | .groupby(sle.batch_no) |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 193 | ) |
| 194 | |
| 195 | if self.sle.name: |
| 196 | query = query.where(sle.name != self.sle.name) |
| 197 | |
| 198 | for d in query.run(as_dict=True): |
Rohit Waghchaure | 40ab3bd | 2023-06-01 16:08:49 +0530 | [diff] [blame] | 199 | self.non_batchwise_balance_value += flt(d.batch_value) |
| 200 | self.non_batchwise_balance_qty += flt(d.batch_qty) |
| 201 | self.available_qty[d.batch_no] += flt(d.batch_qty) |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 202 | |
| 203 | @deprecated |
Rohit Waghchaure | 40ab3bd | 2023-06-01 16:08:49 +0530 | [diff] [blame] | 204 | def set_balance_value_from_bundle(self) -> None: |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 205 | bundle = frappe.qb.DocType("Serial and Batch Bundle") |
| 206 | bundle_child = frappe.qb.DocType("Serial and Batch Entry") |
| 207 | batch = frappe.qb.DocType("Batch") |
| 208 | |
Akhil Narang | 3effaf2 | 2024-03-27 11:37:26 +0530 | [diff] [blame] | 209 | timestamp_condition = CombineDatetime(bundle.posting_date, bundle.posting_time) < CombineDatetime( |
| 210 | self.sle.posting_date, self.sle.posting_time |
| 211 | ) |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 212 | |
| 213 | if self.sle.creation: |
| 214 | timestamp_condition |= ( |
| 215 | CombineDatetime(bundle.posting_date, bundle.posting_time) |
| 216 | == CombineDatetime(self.sle.posting_date, self.sle.posting_time) |
| 217 | ) & (bundle.creation < self.sle.creation) |
| 218 | |
| 219 | query = ( |
| 220 | frappe.qb.from_(bundle) |
| 221 | .inner_join(bundle_child) |
| 222 | .on(bundle.name == bundle_child.parent) |
| 223 | .inner_join(batch) |
| 224 | .on(bundle_child.batch_no == batch.name) |
| 225 | .select( |
Rohit Waghchaure | 40ab3bd | 2023-06-01 16:08:49 +0530 | [diff] [blame] | 226 | bundle_child.batch_no, |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 227 | Sum(bundle_child.qty).as_("batch_qty"), |
Rohit Waghchaure | 40ab3bd | 2023-06-01 16:08:49 +0530 | [diff] [blame] | 228 | Sum(bundle_child.stock_value_difference).as_("batch_value"), |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 229 | ) |
| 230 | .where( |
| 231 | (bundle.item_code == self.sle.item_code) |
| 232 | & (bundle.warehouse == self.sle.warehouse) |
| 233 | & (bundle_child.batch_no.isnotnull()) |
| 234 | & (batch.use_batchwise_valuation == 0) |
| 235 | & (bundle.is_cancelled == 0) |
Rohit Waghchaure | 40ab3bd | 2023-06-01 16:08:49 +0530 | [diff] [blame] | 236 | & (bundle.docstatus == 1) |
Rohit Waghchaure | f8bf4aa | 2023-04-02 13:13:42 +0530 | [diff] [blame] | 237 | & (bundle.type_of_transaction.isin(["Inward", "Outward"])) |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 238 | ) |
| 239 | .where(timestamp_condition) |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 240 | .groupby(bundle_child.batch_no) |
| 241 | ) |
| 242 | |
| 243 | if self.sle.serial_and_batch_bundle: |
| 244 | query = query.where(bundle.name != self.sle.serial_and_batch_bundle) |
| 245 | |
Rohit Waghchaure | 3423334 | 2024-03-21 20:28:16 +0530 | [diff] [blame] | 246 | query = query.where(bundle.voucher_type != "Pick List") |
| 247 | |
Rohit Waghchaure | f704eb7 | 2023-03-30 11:32:39 +0530 | [diff] [blame] | 248 | for d in query.run(as_dict=True): |
Rohit Waghchaure | 40ab3bd | 2023-06-01 16:08:49 +0530 | [diff] [blame] | 249 | self.non_batchwise_balance_value += flt(d.batch_value) |
| 250 | self.non_batchwise_balance_qty += flt(d.batch_qty) |
| 251 | self.available_qty[d.batch_no] += flt(d.batch_qty) |