feat(ekonomi): unify UMKM and Pasar Desa models, add business profile and product forms

This commit is contained in:
2026-04-21 15:19:57 +08:00
parent a2d157ee02
commit e286cb4f2b
22 changed files with 586 additions and 80 deletions

View File

@@ -11,8 +11,12 @@ async function pasarDesaFindMany(context: Context) {
const categoryId = context.query.categoryId as string | undefined;
const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Buat where clause: Tampilkan hanya yang TIDAK punya umkmId (Produk Pasar Murni)
const where: any = {
isActive: true,
deletedAt: null,
umkmId: null
};
// Tambahkan filter kategori (jika ada)
if (categoryId) {
@@ -65,7 +69,7 @@ async function pasarDesaFindMany(context: Context) {
return {
success: true,
message: "Berhasil ambil pasar desa dengan pagination",
message: "Berhasil ambil pasar desa dengan pagination (Non-UMKM)",
data,
page,
limit,

View File

@@ -22,8 +22,9 @@ async function umkmDashboardDetailPenjualan(context: Context) {
where: { periode: periodeLalu, deletedAt: null },
_sum: { totalNilai: true }
}),
prisma.produkUmkm.findMany({
where: { deletedAt: null },
// Use PasarDesa with umkmId filter
prisma.pasarDesa.findMany({
where: { deletedAt: null, umkmId: { not: null } },
select: { id: true, nama: true, stok: true }
})
]);
@@ -33,11 +34,11 @@ async function umkmDashboardDetailPenjualan(context: Context) {
const laluRaw = produkLalu.find(l => l.produkId === p.id)?._sum || { totalNilai: 0 };
const skrg = {
totalNilai: skrgRaw.totalNilai || 0,
jumlah: skrgRaw.jumlah || 0
totalNilai: (skrgRaw as any).totalNilai || 0,
jumlah: (skrgRaw as any).jumlah || 0
};
const lalu = {
totalNilai: laluRaw.totalNilai || 0
totalNilai: (laluRaw as any).totalNilai || 0
};
let trend = "stable";

View File

@@ -20,7 +20,10 @@ async function umkmDashboardRingSummary(context: Context) {
where: { periode: periodeLalu, deletedAt: null },
_sum: { totalNilai: true }
}),
prisma.produkUmkm.count({ where: { isActive: true, deletedAt: null } }),
// Count from PasarDesa with umkmId filter
prisma.pasarDesa.count({
where: { isActive: true, deletedAt: null, umkmId: { not: null } }
}),
prisma.penjualanProduk.count({ where: { periode, deletedAt: null } })
]);

View File

@@ -15,17 +15,18 @@ async function umkmDashboardTopProduk(context: Context) {
});
const data = await Promise.all(topPenjualan.map(async (item) => {
const produk = await prisma.produkUmkm.findUnique({
// Find from PasarDesa now
const produk = await prisma.pasarDesa.findUnique({
where: { id: item.produkId },
include: { umkm: true }
});
return {
namaProduk: produk?.nama || "Unknown",
namaUmkm: produk?.umkm.nama || "Unknown",
namaUmkm: produk?.umkm?.nama || "Unknown",
totalPenjualan: item._sum.totalNilai || 0,
jumlahTerjual: item._sum.jumlah || 0,
growth: 0 // logic growth bisa ditambah jika diperlukan
growth: 0
};
}));

View File

@@ -11,9 +11,9 @@ async function penjualanProdukCreate(context: Context) {
const totalNilai = body.jumlah * body.hargaSatuan;
try {
// Gunakan transaction untuk update stok produk
// Gunakan transaction untuk update stok produk (PasarDesa)
const result = await prisma.$transaction(async (tx) => {
// 1. Catat penjualan
// 1. Catat penjualan (relasi ke PasarDesa)
const penjualan = await tx.penjualanProduk.create({
data: {
produkId: body.produkId,
@@ -26,8 +26,8 @@ async function penjualanProdukCreate(context: Context) {
},
});
// 2. Update stok produk
await tx.produkUmkm.update({
// 2. Update stok di model PasarDesa
await tx.pasarDesa.update({
where: { id: body.produkId },
data: {
stok: {
@@ -41,7 +41,7 @@ async function penjualanProdukCreate(context: Context) {
return {
success: true,
message: "Berhasil mencatat penjualan produk",
message: "Berhasil mencatat penjualan produk (PasarDesa)",
data: result,
};
} catch (e) {

View File

@@ -23,8 +23,8 @@ async function penjualanProdukDelete(context: Context) {
},
});
// 3. Kembalikan stok produk
await tx.produkUmkm.update({
// 3. Kembalikan stok produk ke PasarDesa
await tx.pasarDesa.update({
where: { id: data.produkId },
data: {
stok: {
@@ -38,7 +38,7 @@ async function penjualanProdukDelete(context: Context) {
return {
success: true,
message: "Berhasil menghapus data penjualan dan mengembalikan stok",
message: "Berhasil menghapus data penjualan dan mengembalikan stok (PasarDesa)",
data: result,
};
} catch (e) {

View File

@@ -32,10 +32,10 @@ async function penjualanProdukUpdate(context: Context) {
},
});
// 3. Update stok jika produk sama, sesuaikan selisih
// 3. Update stok di PasarDesa jika produk sama, sesuaikan selisih
if (oldData.produkId === body.produkId) {
const diff = body.jumlah - oldData.jumlah;
await tx.produkUmkm.update({
await tx.pasarDesa.update({
where: { id: body.produkId },
data: {
stok: {
@@ -44,8 +44,8 @@ async function penjualanProdukUpdate(context: Context) {
}
});
} else {
// Jika produk berubah, kembalikan stok lama dan kurangi stok baru
await tx.produkUmkm.update({
// Jika produk berubah, kembalikan stok lama dan kurangi stok baru di PasarDesa
await tx.pasarDesa.update({
where: { id: oldData.produkId },
data: {
stok: {
@@ -53,7 +53,7 @@ async function penjualanProdukUpdate(context: Context) {
}
}
});
await tx.produkUmkm.update({
await tx.pasarDesa.update({
where: { id: body.produkId },
data: {
stok: {
@@ -68,7 +68,7 @@ async function penjualanProdukUpdate(context: Context) {
return {
success: true,
message: "Berhasil memperbarui data penjualan",
message: "Berhasil memperbarui data penjualan (PasarDesa)",
data: result,
};
} catch (e) {

View File

@@ -5,7 +5,7 @@ async function produkUmkmCreate(context: Context) {
const body = context.body as any;
try {
const data = await prisma.produkUmkm.create({
const data = await prisma.pasarDesa.create({
data: {
nama: body.nama,
harga: body.harga,
@@ -14,12 +14,14 @@ async function produkUmkmCreate(context: Context) {
umkmId: body.umkmId,
imageId: body.imageId,
isActive: body.isActive ?? true,
rating: 0, // Default for UMKM products
kategoriProdukId: body.kategoriId, // Now required via PasarDesa
},
});
return {
success: true,
message: "Berhasil membuat produk UMKM baru",
message: "Berhasil membuat produk UMKM baru (PasarDesa)",
data,
};
} catch (e) {

View File

@@ -5,8 +5,8 @@ async function produkUmkmDelete(context: Context) {
const id = context.params.id;
try {
// Soft delete
const data = await prisma.produkUmkm.update({
// Soft delete on PasarDesa
const data = await prisma.pasarDesa.update({
where: { id },
data: {
deletedAt: new Date(),
@@ -16,7 +16,7 @@ async function produkUmkmDelete(context: Context) {
return {
success: true,
message: "Berhasil menghapus produk UMKM",
message: "Berhasil menghapus produk UMKM (PasarDesa)",
data,
};
} catch (e) {

View File

@@ -10,16 +10,19 @@ async function produkUmkmFindMany(context: Context) {
const kategoriId = context.query.kategoriId as string | undefined;
const skip = (page - 1) * limit;
const where: any = { deletedAt: null };
// Filter: ONLY products that belong to an UMKM
const where: any = {
deletedAt: null,
isActive: true,
umkmId: { not: null }
};
if (umkmId) {
where.umkmId = umkmId;
}
if (kategoriId) {
where.umkm = {
kategoriId: kategoriId
};
where.kategoriProdukId = kategoriId;
}
if (search) {
@@ -28,19 +31,20 @@ async function produkUmkmFindMany(context: Context) {
try {
const [data, total] = await Promise.all([
prisma.produkUmkm.findMany({
prisma.pasarDesa.findMany({
where,
include: {
image: true,
umkm: {
include: { kategori: true }
}
},
kategoriProduk: true
},
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.produkUmkm.count({ where }),
prisma.pasarDesa.count({ where }),
]);
return {

View File

@@ -5,7 +5,7 @@ async function produkUmkmFindUnique(context: Context) {
const id = context.params.id;
try {
const data = await prisma.produkUmkm.findUnique({
const data = await prisma.pasarDesa.findUnique({
where: { id },
include: {
image: true,
@@ -14,7 +14,8 @@ async function produkUmkmFindUnique(context: Context) {
where: { deletedAt: null },
orderBy: { tanggal: 'desc' },
take: 10
}
},
kategoriProduk: true
},
});
@@ -27,7 +28,7 @@ async function produkUmkmFindUnique(context: Context) {
return {
success: true,
message: "Berhasil mengambil detail produk UMKM",
message: "Berhasil mengambil detail produk UMKM (PasarDesa)",
data,
};
} catch (e) {

View File

@@ -6,7 +6,7 @@ async function produkUmkmUpdate(context: Context) {
const id = context.params.id;
try {
const data = await prisma.produkUmkm.update({
const data = await prisma.pasarDesa.update({
where: { id },
data: {
nama: body.nama,
@@ -16,12 +16,13 @@ async function produkUmkmUpdate(context: Context) {
umkmId: body.umkmId,
imageId: body.imageId,
isActive: body.isActive,
kategoriProdukId: body.kategoriId, // Now editable via PasarDesa
},
});
return {
success: true,
message: "Berhasil memperbarui produk UMKM",
message: "Berhasil memperbarui produk UMKM (PasarDesa)",
data,
};
} catch (e) {