| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 |
- """Supplier-assignment inheritance for newly created spools (#2988).
- A new spool of a product that other spools already carry supplier
- assignments for should arrive knowing where it can be bought — whether it
- is created manually, via the API, or by the RFID auto-add. Only the source
- list is copied; ``is_purchase_source`` is deliberately reset, because where
- THIS spool was bought is not something the donor can know.
- """
- from sqlalchemy import select
- from sqlalchemy.ext.asyncio import AsyncSession
- from backend.app.models.spool import Spool
- from backend.app.models.supplier import SpoolSupplier
- async def find_supplier_link_templates_for_product(
- db: AsyncSession,
- *,
- material: str | None,
- subtype: str | None,
- brand: str | None,
- color_name: str | None,
- ) -> list[dict]:
- """Return supplier-link field dicts from the newest matching spool.
- Product identity is the (material, subtype, brand, color_name) string
- tuple — the same key FilamentSkuSettings groups by. Archived spools
- count as donors: a product being out of stock doesn't change where it
- can be bought.
- """
- if not material:
- return []
- def _same(column, value):
- return column.is_(None) if value is None else column == value
- donor = await db.execute(
- select(Spool.id)
- .join(SpoolSupplier, SpoolSupplier.spool_id == Spool.id)
- .where(
- Spool.material == material,
- _same(Spool.subtype, subtype),
- _same(Spool.brand, brand),
- _same(Spool.color_name, color_name),
- )
- .order_by(Spool.updated_at.desc())
- .limit(1)
- )
- donor_id = donor.scalars().first()
- if donor_id is None:
- return []
- links = await db.execute(select(SpoolSupplier).where(SpoolSupplier.spool_id == donor_id))
- return [
- {
- "supplier_id": link.supplier_id,
- "supplier_article_number": link.supplier_article_number,
- "quoted_price_per_kg": link.quoted_price_per_kg,
- "is_purchase_source": False,
- }
- for link in links.scalars().all()
- ]
- async def apply_supplier_inheritance(db: AsyncSession, spool: Spool) -> None:
- """Copy supplier assignments onto a freshly created (flushed) spool.
- No-op when the spool already has assignments. The caller commits.
- """
- existing = await db.execute(select(SpoolSupplier.id).where(SpoolSupplier.spool_id == spool.id).limit(1))
- if existing.first() is not None:
- return
- templates = await find_supplier_link_templates_for_product(
- db,
- material=spool.material,
- subtype=spool.subtype,
- brand=spool.brand,
- color_name=spool.color_name,
- )
- _attach(db, spool, templates)
- async def apply_supplier_inheritance_to_batch(db: AsyncSession, spools: list[Spool]) -> None:
- """Inheritance for a bulk create: one donor lookup for the whole batch.
- Every copy is built from the same payload, so the product tuple — and with
- it the answer — is identical for all of them. The caller commits.
- """
- if not spools:
- return
- first = spools[0]
- templates = await find_supplier_link_templates_for_product(
- db,
- material=first.material,
- subtype=first.subtype,
- brand=first.brand,
- color_name=first.color_name,
- )
- for spool in spools:
- _attach(db, spool, templates)
- def _attach(db: AsyncSession, spool: Spool, templates: list[dict]) -> None:
- for template in templates:
- db.add(SpoolSupplier(spool_id=spool.id, **template))
|