|
| 1 | +from odoo import fields, models |
| 2 | + |
| 3 | + |
| 4 | +class ResCompany(models.Model): |
| 5 | + _inherit = "res.company" |
| 6 | + |
| 7 | + def action_close_stock_valuation(self, at_date=None, auto_post=False): |
| 8 | + """Al generar el asiento global de cierre de valorización periódica, |
| 9 | + vincularlo a los ``stock.move`` que valoriza (campo ``account_move_id``), |
| 10 | + igual que los movimientos perpetuos quedan ligados a su asiento. |
| 11 | +
|
| 12 | + De fábrica el asiento de cierre se arma agregado por cuenta contable y |
| 13 | + no queda asociado a ningún movimiento, por lo que en los reportes de |
| 14 | + movimientos de productos no había forma de llegar al asiento que los |
| 15 | + valoró. Guardamos el cierre en el ``account_move_id`` estándar (stored) |
| 16 | + para que el campo navegable ``account_move_ids`` (computado), los filtros |
| 17 | + "Con/Sin Asiento" y el buscador funcionen igual que con la valorización |
| 18 | + perpetua, sin distinguir perpetuo vs. periódico.""" |
| 19 | + self.ensure_one() |
| 20 | + # El corte anterior hay que leerlo ANTES de super(): super() registra |
| 21 | + # este cierre y _get_last_closing_date pasaría a devolver el nuevo. |
| 22 | + previous_closing_date = self._get_last_closing_date() |
| 23 | + action = super().action_close_stock_valuation(at_date=at_date, auto_post=auto_post) |
| 24 | + closing_move = self.env["account.move"] |
| 25 | + if isinstance(action, dict) and action.get("res_model") == "account.move": |
| 26 | + closing_move = self.env["account.move"].browse(action.get("res_id")).exists() |
| 27 | + if not closing_move: |
| 28 | + return action |
| 29 | + |
| 30 | + moves = self._get_periodic_closing_stock_moves(at_date, previous_closing_date) |
| 31 | + # No tocar movimientos que ya tienen un asiento posteado (perpetuos o un |
| 32 | + # cierre previo válido); sí re-vincular si el asiento anterior quedó sin |
| 33 | + # postear (p. ej. un cierre cancelado y regenerado). |
| 34 | + moves = moves.filtered(lambda m: not m.account_move_id or m.account_move_id.state != "posted") |
| 35 | + if moves: |
| 36 | + moves.account_move_id = closing_move.id |
| 37 | + return action |
| 38 | + |
| 39 | + def _get_periodic_closing_stock_moves(self, at_date=None, from_date=None): |
| 40 | + """Movimientos de productos con valorización periódica cubiertos por el |
| 41 | + asiento de cierre, replicando el alcance de |
| 42 | + ``_get_location_valuation_vals``: productos periódicos, dentro del rango |
| 43 | + de fechas del cierre, que entran o salen de ubicaciones valuadas.""" |
| 44 | + self.ensure_one() |
| 45 | + if isinstance(at_date, str): |
| 46 | + at_date = fields.Date.from_string(at_date) |
| 47 | + valued_locations = self.env["stock.location"].search( |
| 48 | + [ |
| 49 | + ("company_id", "in", [self.id, False]), |
| 50 | + ] |
| 51 | + ) |
| 52 | + if not valued_locations: |
| 53 | + return self.env["stock.move"] |
| 54 | + domain = [ |
| 55 | + "|", |
| 56 | + "&", |
| 57 | + ("is_out", "=", True), |
| 58 | + ("location_dest_id", "in", valued_locations.ids), |
| 59 | + "&", |
| 60 | + ("is_in", "=", True), |
| 61 | + ("location_id", "in", valued_locations.ids), |
| 62 | + ("product_id.is_storable", "=", True), |
| 63 | + ("product_id.valuation", "=", "periodic"), |
| 64 | + ("company_id", "=", self.id), |
| 65 | + ] |
| 66 | + if from_date: |
| 67 | + domain.append(("date", ">=", from_date)) |
| 68 | + if at_date: |
| 69 | + domain.append(("date", "<=", at_date)) |
| 70 | + return self.env["stock.move"].search(domain) |
0 commit comments