{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "purchase-order-receiving",
  "title": "Purchase Order Receiving",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/purchase-order-receiving/purchase-order-receiving.component.ts",
      "content": "import { Component, Input, computed, signal, ChangeDetectionStrategy } from '@angular/core'\nimport {\n  AlertTriangle,\n  Clock,\n  LucideAngularModule,\n  PackageCheck,\n  Printer,\n  ShieldCheck,\n  Warehouse,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent, type BadgeVariant } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardFooterComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport {\n  UiSelectComponent,\n  UiSelectContentComponent,\n  UiSelectItemComponent,\n  UiSelectTriggerComponent,\n  UiSelectValueComponent,\n} from '@/ui/select/select.component'\nimport {\n  UiTableBodyComponent,\n  UiTableCellComponent,\n  UiTableComponent,\n  UiTableHeadComponent,\n  UiTableHeaderComponent,\n  UiTableRowComponent,\n} from '@/ui/table/table.component'\n\nexport interface POLineItem {\n  id: string\n  sku: string\n  title: string\n  orderedQty: number\n  receivedQty: number\n  rejectedQty: number\n  rejectionReason: 'none' | 'damaged_packaging' | 'expired_lot' | 'wrong_specification' | 'temperature_excursion'\n  lotNumber: string\n  expiryDate: string\n  stagingLocation: string\n}\n\nexport interface PurchaseOrderReceivingProps {\n  poNumber?: string\n  vendorName?: string\n  deliveryDocket?: string\n  className?: string\n}\n\nexport type InspectionStatus = 'pending' | 'passed' | 'discrepancy'\n\n/**\n * Angular port of the React PurchaseOrderReceiving block: an inbound dock workbench with the\n * PO header (docket, receipt status, print / Generate GRN actions), a receiving bay picker,\n * the accepted-vs-ordered summary and QA disposition, and a line table where each PO line's\n * received / rejected quantities, rejection reason and lot number are logged. Generating the\n * GRN locks the lines and marks the receipt passed or discrepancy. Edit the sample lines below.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-purchase-order-receiving, [ui-purchase-order-receiving]',\n  standalone: true,\n  host: {\n    '[attr.data-slot]': '\"purchase-order-receiving\"',\n    '[class]': 'hostClass',\n  },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardFooterComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiInputComponent,\n    UiSelectComponent,\n    UiSelectContentComponent,\n    UiSelectItemComponent,\n    UiSelectTriggerComponent,\n    UiSelectValueComponent,\n    UiTableBodyComponent,\n    UiTableCellComponent,\n    UiTableComponent,\n    UiTableHeadComponent,\n    UiTableHeaderComponent,\n    UiTableRowComponent,\n  ],\n  template: `\n    <!-- Top PO Header Info -->\n    <div ui-card>\n      <div ui-card-header class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n        <div class=\"space-y-1\">\n          <div class=\"flex items-center gap-2\">\n            <span class=\"text-foreground font-mono text-sm font-semibold\">{{ poNumber }}</span>\n            <span ui-badge variant=\"outline\" class=\"font-mono text-xs\">{{ deliveryDocket }}</span>\n            <span ui-badge [variant]=\"statusVariant()\" class=\"capitalize\">{{\n              isCompleted() ? 'GRN ' + inspectionStatus() : 'Receiving in Progress'\n            }}</span>\n          </div>\n          <h3 ui-card-title class=\"text-xl\">Inbound Goods Receipt &amp; QA Inspection</h3>\n          <p ui-card-description>\n            Supplier: <strong class=\"text-foreground font-medium\">{{ vendorName }}</strong> · Inbound Dock Verification\n          </p>\n        </div>\n\n        <div class=\"flex items-center gap-2\">\n          <button ui-button variant=\"outline\" size=\"sm\" class=\"gap-1.5\">\n            <lucide-icon [img]=\"Printer\" class=\"size-4\" />\n            Print Putaway Labels\n          </button>\n          @if (!isCompleted()) {\n            <button ui-button variant=\"default\" size=\"sm\" class=\"gap-1.5\" (click)=\"finalizeReceipt()\">\n              <lucide-icon [img]=\"PackageCheck\" class=\"size-4\" />\n              Generate GRN\n            </button>\n          } @else {\n            <button ui-button variant=\"secondary\" size=\"sm\" class=\"gap-1.5\" (click)=\"resetReceipt()\">\n              Edit Receipt\n            </button>\n          }\n        </div>\n      </div>\n\n      <div ui-card-content class=\"border-border grid grid-cols-1 gap-4 border-t pt-4 sm:grid-cols-3\">\n        <!-- Receiving Dock -->\n        <div class=\"border-border space-y-1.5 rounded-lg border p-3\">\n          <div class=\"text-muted-foreground flex items-center gap-1.5 text-xs font-medium\">\n            <lucide-icon [img]=\"Warehouse\" class=\"size-3.5\" />\n            <span>RECEIVING BAY</span>\n          </div>\n          <ui-select [value]=\"receivingDock()\" (valueChange)=\"receivingDock.set($event)\" [disabled]=\"isCompleted()\">\n            <button ui-select-trigger class=\"w-full\">\n              <ui-select-value />\n            </button>\n            <ui-select-content>\n              <ui-select-item value=\"dock-01\">Inbound Dock Bay 01 (Heavy Freight)</ui-select-item>\n              <ui-select-item value=\"dock-02\">Inbound Dock Bay 02 (Cross-Dock)</ui-select-item>\n              <ui-select-item value=\"dock-03\">Inbound Dock Bay 03 (Standard Freight)</ui-select-item>\n              <ui-select-item value=\"dock-04\">Inbound Cold Storage Quarantine Bay</ui-select-item>\n            </ui-select-content>\n          </ui-select>\n        </div>\n\n        <!-- Discrepancy Summary -->\n        <div class=\"border-border space-y-1 rounded-lg border p-3\">\n          <div class=\"text-muted-foreground text-xs font-medium\">RECEIPT SUMMARY</div>\n          <div class=\"flex items-baseline justify-between pt-1\">\n            <div class=\"font-mono text-xl font-semibold\">{{ totalAccepted() }} / {{ totalOrdered() }}</div>\n            @if (totalRejected() > 0) {\n              <span ui-badge variant=\"destructive\" class=\"font-mono text-xs\">{{ totalRejected() }} Rejected</span>\n            } @else {\n              <span ui-badge variant=\"outline\" class=\"text-muted-foreground font-mono text-xs\">100% Match</span>\n            }\n          </div>\n        </div>\n\n        <!-- QA Verification Badge -->\n        <div class=\"border-border space-y-1 rounded-lg border p-3\">\n          <div class=\"text-muted-foreground text-xs font-medium\">QA AUDIT DISPOSITION</div>\n          <div class=\"flex items-center gap-2 pt-1\">\n            @if (inspectionStatus() === 'passed') {\n              <lucide-icon [img]=\"ShieldCheck\" class=\"text-foreground size-5\" />\n            }\n            @if (inspectionStatus() === 'discrepancy') {\n              <lucide-icon [img]=\"AlertTriangle\" class=\"text-destructive size-5\" />\n            }\n            @if (inspectionStatus() === 'pending') {\n              <lucide-icon [img]=\"Clock\" class=\"text-muted-foreground size-5\" />\n            }\n            <span class=\"text-sm font-medium\">{{ dispositionLabel() }}</span>\n          </div>\n        </div>\n      </div>\n    </div>\n\n    <!-- PO Line Item Matching Table -->\n    <div ui-card>\n      <div ui-card-header class=\"pb-3\">\n        <h3 ui-card-title class=\"text-base font-semibold\">Purchase Order Line Verification</h3>\n        <p ui-card-description>\n          Verify quantities received against vendor packing list and assign warehouse staging zones.\n        </p>\n      </div>\n\n      <div ui-card-content class=\"p-0\">\n        <div ui-table>\n          <thead ui-table-header>\n            <tr ui-table-row>\n              <th ui-table-head class=\"w-[280px]\">Item / Description</th>\n              <th ui-table-head class=\"text-right\">Ordered</th>\n              <th ui-table-head class=\"w-[110px] text-right\">Received</th>\n              <th ui-table-head class=\"w-[110px] text-right\">Rejected</th>\n              <th ui-table-head>Rejection Reason</th>\n              <th ui-table-head>Lot Number</th>\n              <th ui-table-head>Staging Target</th>\n            </tr>\n          </thead>\n          <tbody ui-table-body>\n            @for (line of lines(); track line.id) {\n              <tr ui-table-row>\n                <!-- Item Details -->\n                <td ui-table-cell>\n                  <div class=\"font-medium\">{{ line.title }}</div>\n                  <div class=\"text-muted-foreground font-mono text-xs\">{{ line.sku }}</div>\n                </td>\n\n                <!-- Ordered Qty -->\n                <td ui-table-cell class=\"text-muted-foreground text-right font-mono text-sm\">\n                  {{ line.orderedQty }}\n                </td>\n\n                <!-- Received Qty -->\n                <td ui-table-cell class=\"text-right\">\n                  <ui-input\n                    type=\"number\"\n                    [min]=\"0\"\n                    [value]=\"line.receivedQty\"\n                    (valueChange)=\"updateLine(line.id, { receivedQty: toNumber($event) })\"\n                    class=\"h-8 text-right font-mono\"\n                    [disabled]=\"isCompleted()\"\n                  />\n                </td>\n\n                <!-- Rejected Qty -->\n                <td ui-table-cell class=\"text-right\">\n                  <ui-input\n                    type=\"number\"\n                    [min]=\"0\"\n                    [value]=\"line.rejectedQty\"\n                    (valueChange)=\"updateLine(line.id, { rejectedQty: toNumber($event) })\"\n                    class=\"h-8 text-right font-mono\"\n                    [disabled]=\"isCompleted()\"\n                  />\n                </td>\n\n                <!-- Reason -->\n                <td ui-table-cell>\n                  <ui-select\n                    [value]=\"line.rejectionReason\"\n                    (valueChange)=\"setRejectionReason(line.id, $event)\"\n                    [disabled]=\"isCompleted() || line.rejectedQty === 0\"\n                  >\n                    <button ui-select-trigger class=\"h-8 w-[160px] text-xs\">\n                      <ui-select-value />\n                    </button>\n                    <ui-select-content>\n                      <ui-select-item value=\"none\">None (Accepted)</ui-select-item>\n                      <ui-select-item value=\"damaged_packaging\">Damaged Packaging</ui-select-item>\n                      <ui-select-item value=\"expired_lot\">Short Shelf Life</ui-select-item>\n                      <ui-select-item value=\"wrong_specification\">Incorrect Spec</ui-select-item>\n                      <ui-select-item value=\"temperature_excursion\">Temp Excursion</ui-select-item>\n                    </ui-select-content>\n                  </ui-select>\n                </td>\n\n                <!-- Lot Number Input -->\n                <td ui-table-cell>\n                  <ui-input\n                    [value]=\"line.lotNumber\"\n                    (valueChange)=\"updateLine(line.id, { lotNumber: $event })\"\n                    placeholder=\"Lot #\"\n                    class=\"h-8 w-28 font-mono text-xs\"\n                    [disabled]=\"isCompleted()\"\n                  />\n                </td>\n\n                <!-- Staging Target -->\n                <td ui-table-cell>\n                  <span class=\"bg-muted text-foreground rounded px-2 py-1 font-mono text-xs font-medium\">{{\n                    line.stagingLocation\n                  }}</span>\n                </td>\n              </tr>\n            }\n          </tbody>\n        </div>\n      </div>\n\n      <div\n        ui-card-footer\n        class=\"border-border text-muted-foreground flex items-center justify-between border-t py-3 text-xs\"\n      >\n        <div>Official GRN generates supplier debit note automatically for any non-zero rejection.</div>\n        <div class=\"text-foreground font-medium\">\n          Accepted: <span class=\"font-mono font-semibold\">{{ totalAccepted() }}</span> units · Rejected:\n          <span class=\"text-destructive font-mono font-semibold\">{{ totalRejected() }}</span> units\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiPurchaseOrderReceivingComponent {\n  @Input() poNumber = 'PO-2026-4409'\n  @Input() vendorName = 'Apex Precision Engineering Ltd'\n  @Input() deliveryDocket = 'BOL-APX-98214'\n  @Input('class') className?: string\n\n  protected readonly AlertTriangle = AlertTriangle\n  protected readonly Clock = Clock\n  protected readonly PackageCheck = PackageCheck\n  protected readonly Printer = Printer\n  protected readonly ShieldCheck = ShieldCheck\n  protected readonly Warehouse = Warehouse\n\n  readonly isCompleted = signal(false)\n  readonly receivingDock = signal('dock-03')\n  readonly inspectionStatus = signal<InspectionStatus>('pending')\n\n  readonly lines = signal<POLineItem[]>([\n    {\n      id: 'po-1',\n      sku: 'SKU-VALVE-99',\n      title: 'High-Pressure Hydraulic Valve 3/8\"',\n      orderedQty: 100,\n      receivedQty: 100,\n      rejectedQty: 0,\n      rejectionReason: 'none',\n      lotNumber: 'LOT-HYD-998',\n      expiryDate: '2029-12-31',\n      stagingLocation: 'ZONE-A-RACK-04',\n    },\n    {\n      id: 'po-2',\n      sku: 'SKU-SEAL-04',\n      title: 'Fluorocarbon O-Ring Flange Kit (Pack of 50)',\n      orderedQty: 250,\n      receivedQty: 245,\n      rejectedQty: 5,\n      rejectionReason: 'damaged_packaging',\n      lotNumber: 'LOT-O-7712',\n      expiryDate: '2028-06-30',\n      stagingLocation: 'ZONE-A-RACK-01',\n    },\n    {\n      id: 'po-3',\n      sku: 'SKU-GAUGE-12',\n      title: 'Digital Calibration Pressure Gauge 0-600 PSI',\n      orderedQty: 40,\n      receivedQty: 40,\n      rejectedQty: 0,\n      rejectionReason: 'none',\n      lotNumber: 'LOT-CAL-002',\n      expiryDate: '2031-01-15',\n      stagingLocation: 'ZONE-B-RACK-09',\n    },\n  ])\n\n  readonly totalOrdered = computed(() => this.lines().reduce((acc, item) => acc + item.orderedQty, 0))\n  readonly totalAccepted = computed(() => this.lines().reduce((acc, item) => acc + item.receivedQty, 0))\n  readonly totalRejected = computed(() => this.lines().reduce((acc, item) => acc + item.rejectedQty, 0))\n\n  readonly statusVariant = computed<BadgeVariant>(() => {\n    const status = this.inspectionStatus()\n    return status === 'passed' ? 'default' : status === 'discrepancy' ? 'destructive' : 'secondary'\n  })\n\n  readonly dispositionLabel = computed(() => {\n    const status = this.inspectionStatus()\n    return status === 'passed'\n      ? 'All lines cleared for putaway'\n      : status === 'discrepancy'\n        ? 'Discrepancy logged for vendor credit'\n        : 'Awaiting line verification'\n  })\n\n  get hostClass(): string {\n    return cn('block w-full space-y-6', this.className)\n  }\n\n  updateLine(id: string, updates: Partial<POLineItem>): void {\n    this.lines.update((prev) => prev.map((line) => (line.id === id ? { ...line, ...updates } : line)))\n  }\n\n  setRejectionReason(id: string, value: string): void {\n    this.updateLine(id, { rejectionReason: value as POLineItem['rejectionReason'] })\n  }\n\n  protected toNumber(value: string): number {\n    return Number(value)\n  }\n\n  finalizeReceipt(): void {\n    if (this.totalRejected() > 0 || this.totalAccepted() < this.totalOrdered()) {\n      this.inspectionStatus.set('discrepancy')\n    } else {\n      this.inspectionStatus.set('passed')\n    }\n    this.isCompleted.set(true)\n  }\n\n  resetReceipt(): void {\n    this.isCompleted.set(false)\n    this.inspectionStatus.set('pending')\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/purchase-order-receiving/purchase-order-receiving.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/purchase-order-receiving/index.ts",
      "content": "export { UiPurchaseOrderReceivingComponent } from './purchase-order-receiving.component'\nexport type { InspectionStatus, POLineItem, PurchaseOrderReceivingProps } from './purchase-order-receiving.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/purchase-order-receiving/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/input.json",
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/select.json",
    "https://uipkge.dev/r/angular/table.json"
  ],
  "description": "Inbound dock receiving workbench with PO matching, QA discrepancy logging, lot registration, and GRN generation.",
  "categories": [
    "dashboard",
    "data-display"
  ]
}