スキル一覧に戻る
Qmop1967

tsh-stock

by Qmop1967

0🍴 0📅 2026年1月24日
GitHubで見るManusで実行

SKILL.md


name: tsh-stock description: | TSH stock and warehouse logic for Main WareHouse. Use when: (1) Displaying stock levels on product pages (2) Understanding warehouse configuration (3) Debugging stock display issues (showing 0 when shouldn't) (4) Working with "In Stock" / "Out of Stock" badges (5) Implementing stock-based filtering (6) Understanding stock sync architecture (webhooks, cron, manual) (7) Understanding the difference between list and detail page stock

TSH Stock Rules

Warehouse Configuration

SettingValue
Warehouse NameMain WareHouse
Warehouse ID2646610000000077024
Business LocationMain TSH Business
Stock TypeAccounting Stock (Available for Sale)
Fieldlocation_available_for_sale_stock
Arraylocations (NOT warehouses)

Stock Formula

Available for Sale = Stock on Hand - Committed Stock
  • Stock on Hand: Physical stock in warehouse
  • Committed Stock: Reserved for pending orders
  • Available for Sale: What can actually be sold

Critical: Correct Field Names

// CORRECT - Use these
const WHOLESALE_LOCATION_NAME = 'Main WareHouse';

const location = item.locations?.find(
  loc => loc.location_name === WHOLESALE_LOCATION_NAME
);
const stock = location?.location_available_for_sale_stock;

// WRONG - Never use
item.warehouses                    // Wrong array name
warehouse_available_stock          // Wrong field name
stock_on_hand                      // Includes committed stock
item.available_stock               // Combines ALL warehouses

Code Implementation

Constants

// src/lib/zoho/stock-cache.ts & src/lib/zoho/products.ts
const WHOLESALE_LOCATION_NAME = 'Main WareHouse';
const WHOLESALE_WAREHOUSE_ID = '2646610000000077024';

Stock Extraction Function

function getWholesaleAvailableStock(item: ZohoItemWithLocations): number {
  if (item.locations && item.locations.length > 0) {
    const wholesaleLocation = item.locations.find(
      (loc) => loc.location_name === WHOLESALE_LOCATION_NAME
    );

    if (wholesaleLocation) {
      return wholesaleLocation.location_available_for_sale_stock || 0;
    }
  }
  // NEVER fall back to item.available_stock (combines all warehouses)
  return 0;
}

Unified Stock Functions (ALWAYS USE)

// For single item (detail page)
const { stock, source } = await getUnifiedStock(itemId, {
  fetchOnMiss: true,
  context: 'product-detail',
});

// For multiple items (list page)
const stockMap = await getUnifiedStockBulk(itemIds, {
  context: 'shop-list',
});

Type Definition

interface ZohoItem {
  item_id: string;
  name: string;
  available_stock: number;
  locations?: Array<{
    location_id: string;
    location_name: string;
    location_stock_on_hand: number;
    location_available_for_sale_stock: number;
  }>;
}

List vs Detail Page

Detail Page (Accurate Stock)

  • Endpoint: GET /inventory/v1/items/{item_id}
  • Returns: Full item with locations array
  • Stock: Accurate per-warehouse breakdown
export async function getProduct(itemId: string) {
  const response = await zohoFetch(`/inventory/v1/items/${itemId}`, {
    params: { organization_id: process.env.ZOHO_ORGANIZATION_ID },
  });

  const item = response.item;
  const stock = getWholesaleAvailableStock(item);
  return { ...item, stock };
}

List Page (Filtered Stock)

  • Endpoint: GET /inventory/v1/items
  • Note: Does NOT return locations array
  • Use warehouse_id parameter to filter
export async function getAllProductsComplete() {
  const response = await zohoFetch('/inventory/v1/items', {
    params: {
      organization_id: process.env.ZOHO_ORGANIZATION_ID,
      warehouse_id: WHOLESALE_WAREHOUSE_ID,
      status: 'active',
    },
  });

  return response.items.map(item => ({
    ...item,
    stock: item.available_stock ?? 0,
  }));
}

Stock Badge Component

'use client';
import { Badge } from '@/components/ui/badge';
import { useTranslations } from 'next-intl';

export function StockBadge({ stock }: { stock: number }) {
  const t = useTranslations('products');

  if (stock > 10) {
    return <Badge variant="default">{t('inStock')}</Badge>;
  }

  if (stock > 0) {
    return (
      <Badge variant="warning">
        {t('lowStock', { count: stock })}
      </Badge>
    );
  }

  return <Badge variant="secondary">{t('outOfStock')}</Badge>;
}

Translations

// en.json
{
  "products": {
    "inStock": "In Stock",
    "outOfStock": "Out of Stock",
    "lowStock": "Only {count} left",
    "available": "available"
  }
}

// ar.json
{
  "products": {
    "inStock": "متوفر",
    "outOfStock": "غير متوفر",
    "lowStock": "متبقي {count} فقط",
    "available": "متاح"
  }
}

Product Card with Stock

export function ProductCard({ product }) {
  const t = useTranslations('products');

  return (
    <Card>
      <CardContent>
        <h3>{product.name}</h3>
        <div className="flex items-center gap-2 mt-2">
          {product.stock > 0 ? (
            <>
              <span className="text-green-600">{t('inStock')}</span>
              <span className="text-sm text-muted-foreground">
                ({product.stock} {t('available')})
              </span>
            </>
          ) : (
            <span className="text-red-600">{t('outOfStock')}</span>
          )}
        </div>
      </CardContent>
    </Card>
  );
}

Troubleshooting

IssueCauseFix
Stock shows 0Wrong fieldUse location_available_for_sale_stock
Stock shows 0Wrong arrayUse locations not warehouses
Stock shows 0Wrong warehouseUse ID 2646610000000077024
Stock shows 0All committedStock reserved for orders
Not updatingCache staleRevalidate products cache
Different list/detailList lacks locationsExpected - use warehouse_id filter

Debug Commands

# Check stock data
curl "https://www.tsh.sale/api/debug/stock"

# Revalidate product cache
curl "https://www.tsh.sale/api/revalidate?tag=products&secret=tsh-revalidate-2024"

Other Locations (DO NOT USE FOR THIS CONSOLE)

LocationTypePurposeUse
Main WareHouseWarehouseB2B wholesale✅ THIS CONSOLE
Dora StoreWarehouseRetail shop❌ EndUser Console
inactive 1/2WarehouseInactive❌ Delete

Only use Main WareHouse (2646610000000077024) for this B2B console.

Stock Sync Commands

# Check cache status
curl "https://www.tsh.sale/api/sync/stock?action=status"

# Force full sync
curl "https://www.tsh.sale/api/sync/stock?action=sync&secret=tsh-stock-sync-2024&force=true"

Checklist

  • Use getUnifiedStock() or getUnifiedStockBulk() for stock retrieval
  • NEVER use item.available_stock directly
  • Handle 0 stock with "Out of Stock" badge
  • Add stock translations (en.json, ar.json)
  • Verify list/detail stock consistency after changes

スコア

総合スコア

50/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

レビュー

💬

レビュー機能は近日公開予定です