Data Seeding#

Atomberg receives costing sheets from suppliers as Excel files. Each supplier has their own format. We have management commands that read these files and load the data into the database.

How It Works (The Big Picture)#

Excel file from supplier
  Management command parses it
  Creates/finds: Supplier, Part, PartSupplier
  Creates: Values (prices, weights, costs)
  Creates: ViewTemplate + ViewTemplateValues (display layout)

Every command:

  1. Reads the Excel file using openpyxl

  2. Parses part codes, names, categories, and numeric values

  3. Creates database records (idempotent — safe to re-run)

  4. Links formula values to their dependencies

  5. Builds view templates for the frontend

Commands by Category#

Plastic Parts#

Command: bulk_upload_plastic Files: 14 supplier sheets in _raw_data/plastic/ Format: Flat table — one row per part, columns for each cost component

# Import all 14 files
python manage.py bulk_upload_plastic --dir backend/_raw_data/plastic/

# Import a single file
python manage.py bulk_upload_plastic --file backend/_raw_data/plastic/Apex_...xlsx

# Preview without writing to DB
python manage.py bulk_upload_plastic --dir backend/_raw_data/plastic/ --dry-run

What it imports: 407 parts across 14 suppliers with ~35 cost components each (gross weight, RM rate, process cost, BOP cost, freight, PO price, etc.)

Smart features:

  • Auto-detects sheet name (case-insensitive: “New Format”, “NEW FORMAT”, “NEW SUMMARY”)

  • Auto-detects header row (varies by supplier: row 5, 7, or 8)

  • Auto-detects supplier info from rows 1-4 (checks both col A and col B)

  • Auto-generates supplier codes when missing

  • Handles header typos (for example, “Mould Maintanance” → mouldMaintenance)

  • Links formula values (for example, existingRmCost = (rmRate*(1-mbPercent)+mbCost)*grossWt/1000)

Supplier

Code

Parts

Apex Engineers

602035

32

Atharva Polymer

602072

48

Future Finishers

603276

9

Microplast

MICROPLAST

20

Megaklc

MEGAKLC

18

PG Electroplast

602741

59

Pro-Mould

601555

24

Pune Polymer

602480

141

Ramashri

601592

64

Shree Varad

601244

37

Sunshine

603720

1

EPS (Expanded Polystyrene)#

Command: bulk_upload_eps File: _raw_data/eps/EPS costing.xlsx Format: 6 supplier sheets in one workbook, each with different column layouts

python manage.py bulk_upload_eps
python manage.py bulk_upload_eps --dry-run

What it imports: 44 parts across 5 suppliers (Styrotech, Kamaksha, KK Nag, Stypack, Hakimuddin). Skips Thermofoam (summary-only sheet).

Special handling: KK Nag has hierarchical rows (Model → Top/Bottom) with no explicit part codes — codes are auto-generated like GV3-TOP, GV3-BOTTOM.

Corrugated Boxes (Boxline)#

Command: bulk_upload_boxline File: _raw_data/boxline-foam/Boxline- Apr Cost (Ok).xlsx Format: Flat table with 94 box products

python manage.py bulk_upload_boxline
python manage.py bulk_upload_boxline --dry-run

What it imports: 94 box configurations with dimensions (L/W/H), ply count, flute types, and final box cost. Categories include “RSC Box” and “Blade Box”.

Foam (Kamaksha)#

Command: bulk_upload_foam File: _raw_data/foam/Costing - Kamaksha Foam (Ok).xlsx Format: Transposed — part codes are columns, cost components are rows (opposite of plastic!)

python manage.py bulk_upload_foam
python manage.py bulk_upload_foam --dry-run

What it imports: 12 foam packaging parts with 20 cost components each (dimensions, sheet cost, labor, overhead, rejection, ICC, profit, transport).

Tape (Tackon)#

Command: bulk_upload_tape File: _raw_data/tape/Tackon Tape costing.xlsx Format: “Cost Impact” summary sheet with 6 BOPP tape products

python manage.py bulk_upload_tape
python manage.py bulk_upload_tape --dry-run

What it imports: 6 tape products with current price, revised price, April volume, and cost impact.

Polybag & Foam Bag (Wrybillpack)#

Command: bulk_upload_polybag File: _raw_data/polybag/B2C Wrybillpack - Polybag & foam bag costing.xlsx Format: Two sheets — foam bags and polybags with dimensions and pricing

python manage.py bulk_upload_polybag
python manage.py bulk_upload_polybag --dry-run

What it imports: 3 foam bags + 1 polybag with dimensions, material weights, and cost breakdowns.

Washer (Chinar International)#

Command: bulk_upload_washer File: _raw_data/washer/M8 & M3 Washer Updated Cost Sheets.xlsx Format: Vertical layout — 1 part per sheet, cost components in specific rows

python manage.py bulk_upload_washer
python manage.py bulk_upload_washer --dry-run

What it imports: 2 washer parts (M8 and M3) with weights, material rates, and cost per piece.

Shaft (ATPL)#

Command: bulk_upload_atpl File: _raw_data/atpl/ATPL Working.xlsx Format: Vertical layout with 2 suppliers side-by-side per part

python manage.py bulk_upload_atpl
python manage.py bulk_upload_atpl --dry-run

What it imports: 2 shaft parts (Aris and Panda), each with 2 suppliers (Moon Engitech and ATPL) — so 4 PartSupplier records total. Compares material costs, process costs, and overheads.

Casting (Elicast)#

Command: bulk_upload_casting File: _raw_data/casting/Costing Sample_Castings.xlsx Format: Multi-sheet vertical breakdown for a single cast part

python manage.py bulk_upload_casting
python manage.py bulk_upload_casting --dry-run

What it imports: 1 casting part (A428502 - Canary Top Machined) with ~41 cost components scanned from the “Part Cost” sheet (RM cost, die casting, machining, buffing, overhead, profit, etc.)

Database Totals (After All Imports)#

Metric

Count

Parts

567

PartSuppliers

634

Values

16,289

ViewTemplates

623

Suppliers

26+

Categories

40+

Adding New Supplier Data#

When a new costing sheet arrives:

  1. Check the format — Does it match an existing command’s format?

  2. If yes: Place the file in the right _raw_data/ folder and run the command

  3. If no: Create a new management command following the same patterns

  4. Always dry-run first: Use --dry-run to preview what will be created

  5. Safe to re-run: All commands are idempotent (they skip existing records)

Key patterns for new commands:#

# Supplier lookup (code is NOT unique)
supplier = Supplier.objects.filter(code=code).first()

# PartSupplier lookup (no unique constraint)
ps = PartSupplier.objects.filter(part=part, supplier=supplier).first()

# ValueHead, CategoryType, Part (have unique constraints)
head, created = ValueHead.objects.get_or_create(name=name, defaults={...})