mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-06-09 15:07:49 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f64839dfe1 | |||
| 0ff720453f | |||
| ef2f9568ad | |||
| badbe4086a | |||
| 6528739bfd | |||
| 68bddd5c78 | |||
| bfef144668 |
@@ -72,6 +72,7 @@ func (u *DeliveryOrdersController) GetAll(c *fiber.Ctx) error {
|
|||||||
MarketingId: uint(c.QueryInt("marketing_id", 0)),
|
MarketingId: uint(c.QueryInt("marketing_id", 0)),
|
||||||
ProjectFlockID: uint(c.QueryInt("project_flock_id", 0)),
|
ProjectFlockID: uint(c.QueryInt("project_flock_id", 0)),
|
||||||
ProjectFlockKandangID: uint(c.QueryInt("project_flock_kandang_id", 0)),
|
ProjectFlockKandangID: uint(c.QueryInt("project_flock_kandang_id", 0)),
|
||||||
|
WarehouseID: uint(c.QueryInt("warehouse_id", 0)),
|
||||||
SortBy: sortBy,
|
SortBy: sortBy,
|
||||||
SortOrder: sortOrder,
|
SortOrder: sortOrder,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,23 +70,26 @@ func buildMarketingExportWorkbook(items []dto.MarketingListDTO) ([]byte, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setMarketingExportColumns(file *excelize.File, sheet string) error {
|
func setMarketingExportColumns(file *excelize.File, sheet string) error {
|
||||||
|
// A–Q = 17 columns
|
||||||
|
// E = Sales (new), H = Gudang (new), Satuan (old I) removed
|
||||||
columnWidths := map[string]float64{
|
columnWidths := map[string]float64{
|
||||||
"A": 16,
|
"A": 16, // No. Order
|
||||||
"B": 14,
|
"B": 14, // Tanggal
|
||||||
"C": 18,
|
"C": 18, // Status
|
||||||
"D": 20,
|
"D": 20, // Customer
|
||||||
"E": 14,
|
"E": 20, // Sales (new)
|
||||||
"F": 40,
|
"F": 14, // Tipe
|
||||||
"G": 10,
|
"G": 40, // Nama Produk
|
||||||
"H": 12,
|
"H": 20, // Gudang (new)
|
||||||
"I": 12,
|
"I": 10, // Week
|
||||||
"J": 12,
|
"J": 12, // Jumlah
|
||||||
"K": 16,
|
"K": 12, // Qty Peti
|
||||||
"L": 16,
|
"L": 16, // Berat Rata-rata (kg)
|
||||||
"M": 18,
|
"M": 16, // Total Berat (kg)
|
||||||
"N": 18,
|
"N": 18, // Harga Satuan
|
||||||
"O": 18,
|
"O": 18, // Total Harga
|
||||||
"P": 24,
|
"P": 18, // Grand Total
|
||||||
|
"Q": 24, // Catatan
|
||||||
}
|
}
|
||||||
|
|
||||||
for col, width := range columnWidths {
|
for col, width := range columnWidths {
|
||||||
@@ -104,22 +107,23 @@ func setMarketingExportColumns(file *excelize.File, sheet string) error {
|
|||||||
|
|
||||||
func setMarketingExportHeaders(file *excelize.File, sheet string) error {
|
func setMarketingExportHeaders(file *excelize.File, sheet string) error {
|
||||||
headers := []string{
|
headers := []string{
|
||||||
"No. Order", // A
|
"No. Order", // A
|
||||||
"Tanggal", // B
|
"Tanggal", // B
|
||||||
"Status", // C
|
"Status", // C
|
||||||
"Customer", // D
|
"Customer", // D
|
||||||
"Tipe", // E
|
"Sales", // E (new)
|
||||||
"Nama Produk", // F
|
"Tipe", // F
|
||||||
"Week", // G
|
"Nama Produk", // G
|
||||||
"Jumlah", // H
|
"Gudang", // H (new)
|
||||||
"Satuan", // I
|
"Week", // I
|
||||||
"Qty Peti", // J
|
"Jumlah Butir", // J
|
||||||
"Berat Rata-rata (kg)", // K
|
"Qty Peti", // K
|
||||||
"Total Berat (kg)", // L
|
"Berat Rata-rata (kg)", // L
|
||||||
"Harga Satuan", // M
|
"Total Berat (kg)", // M
|
||||||
"Total Harga", // N
|
"Harga Satuan", // N
|
||||||
"Grand Total", // O
|
"Total Harga", // O
|
||||||
"Catatan", // P
|
"Grand Total", // P
|
||||||
|
"Catatan", // Q
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, header := range headers {
|
for i, header := range headers {
|
||||||
@@ -148,7 +152,7 @@ func setMarketingExportHeaders(file *excelize.File, sheet string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return file.SetCellStyle(sheet, "A1", "P1", headerStyle)
|
return file.SetCellStyle(sheet, "A1", "Q1", headerStyle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setMarketingExportRows(file *excelize.File, sheet string, items []dto.MarketingListDTO) error {
|
func setMarketingExportRows(file *excelize.File, sheet string, items []dto.MarketingListDTO) error {
|
||||||
@@ -162,17 +166,161 @@ func setMarketingExportRows(file *excelize.File, sheet string, items []dto.Marke
|
|||||||
soDate := formatMarketingExportDate(item.SoDate)
|
soDate := formatMarketingExportDate(item.SoDate)
|
||||||
status := formatMarketingExportStatus(item)
|
status := formatMarketingExportStatus(item)
|
||||||
customer := safeMarketingExportText(item.Customer.Name)
|
customer := safeMarketingExportText(item.Customer.Name)
|
||||||
grandTotal := sumMarketingGrandTotal(item.SalesOrder)
|
|
||||||
notes := safeMarketingExportText(item.Notes)
|
notes := safeMarketingExportText(item.Notes)
|
||||||
|
salesPerson := safeMarketingExportText(item.SalesPerson.Name)
|
||||||
|
|
||||||
|
isDeliveryOrder := strings.EqualFold(strings.TrimSpace(status), "delivery order")
|
||||||
|
|
||||||
|
// ── Delivery Order branch ──────────────────────────────────────────────
|
||||||
|
if isDeliveryOrder {
|
||||||
|
grandTotal := sumDeliveryGrandTotal(item.DeliveryOrder)
|
||||||
|
|
||||||
|
if len(item.DeliveryOrder) == 0 {
|
||||||
|
row++
|
||||||
|
r := strconv.Itoa(row)
|
||||||
|
vals := map[string]interface{}{
|
||||||
|
"A": soNumber, "B": soDate, "C": status, "D": customer, "E": salesPerson,
|
||||||
|
"F": "-", "G": "-", "H": "-", "I": "-", "J": "-", "K": "-",
|
||||||
|
"L": "-", "M": "-", "N": "-", "O": "-",
|
||||||
|
"P": grandTotal, "Q": notes,
|
||||||
|
}
|
||||||
|
for col, val := range vals {
|
||||||
|
if err := file.SetCellValue(sheet, col+r, val); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build lookup map: MarketingProductId → SO product (for Week & MarketingType)
|
||||||
|
soProductMap := make(map[uint]*dto.DeliveryMarketingProductDTO, len(item.SalesOrder))
|
||||||
|
for i := range item.SalesOrder {
|
||||||
|
soProductMap[item.SalesOrder[i].Id] = &item.SalesOrder[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, group := range item.DeliveryOrder {
|
||||||
|
doNumber := safeMarketingExportText(group.DoNumber)
|
||||||
|
|
||||||
|
doDate := "-"
|
||||||
|
if group.DeliveryDate != nil {
|
||||||
|
doDate = formatMarketingExportDate(*group.DeliveryDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
gudang := "-"
|
||||||
|
if group.Warehouse != nil {
|
||||||
|
gudang = safeMarketingExportText(group.Warehouse.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(group.Deliveries) == 0 {
|
||||||
|
row++
|
||||||
|
r := strconv.Itoa(row)
|
||||||
|
vals := map[string]interface{}{
|
||||||
|
"A": doNumber, "B": doDate, "C": status, "D": customer, "E": salesPerson,
|
||||||
|
"F": "-", "G": "-", "H": gudang, "I": "-", "J": "-", "K": "-",
|
||||||
|
"L": "-", "M": "-", "N": "-", "O": "-",
|
||||||
|
"P": grandTotal, "Q": notes,
|
||||||
|
}
|
||||||
|
for col, val := range vals {
|
||||||
|
if err := file.SetCellValue(sheet, col+r, val); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, delivery := range group.Deliveries {
|
||||||
|
row++
|
||||||
|
r := strconv.Itoa(row)
|
||||||
|
|
||||||
|
productName := "-"
|
||||||
|
if delivery.ProductWarehouse != nil && delivery.ProductWarehouse.Product != nil {
|
||||||
|
if n := strings.TrimSpace(delivery.ProductWarehouse.Product.Name); n != "" {
|
||||||
|
productName = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
week := "-"
|
||||||
|
marketingType := "-"
|
||||||
|
if soProduct, ok := soProductMap[delivery.MarketingProductId]; ok {
|
||||||
|
if soProduct.Week != nil {
|
||||||
|
week = strconv.Itoa(*soProduct.Week)
|
||||||
|
}
|
||||||
|
marketingType = safeMarketingExportText(soProduct.MarketingType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := file.SetCellValue(sheet, "A"+r, doNumber); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, "B"+r, doDate); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, "C"+r, status); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, "D"+r, customer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, "E"+r, salesPerson); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, "F"+r, marketingType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, "G"+r, productName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, "H"+r, gudang); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, "I"+r, week); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, "J"+r, delivery.Qty); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if delivery.TotalPeti != nil {
|
||||||
|
if err := file.SetCellValue(sheet, "K"+r, *delivery.TotalPeti); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := file.SetCellValue(sheet, "K"+r, "-"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, "L"+r, delivery.AvgWeight); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, "M"+r, delivery.TotalWeight); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, "N"+r, delivery.UnitPrice); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, "O"+r, delivery.TotalPrice); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, "P"+r, grandTotal); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, "Q"+r, notes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Sales Order branch (all other statuses) ───────────────────────────
|
||||||
|
grandTotal := sumMarketingGrandTotal(item.SalesOrder)
|
||||||
|
|
||||||
if len(item.SalesOrder) == 0 {
|
if len(item.SalesOrder) == 0 {
|
||||||
row++
|
row++
|
||||||
r := strconv.Itoa(row)
|
r := strconv.Itoa(row)
|
||||||
vals := map[string]interface{}{
|
vals := map[string]interface{}{
|
||||||
"A": soNumber, "B": soDate, "C": status, "D": customer,
|
"A": soNumber, "B": soDate, "C": status, "D": customer, "E": salesPerson,
|
||||||
"E": "-", "F": "-", "G": "-", "H": "-", "I": "-", "J": "-",
|
"F": "-", "G": "-", "H": "-", "I": "-", "J": "-", "K": "-",
|
||||||
"K": "-", "L": "-", "M": "-", "N": "-",
|
"L": "-", "M": "-", "N": "-", "O": "-",
|
||||||
"O": grandTotal, "P": notes,
|
"P": grandTotal, "Q": notes,
|
||||||
}
|
}
|
||||||
for col, val := range vals {
|
for col, val := range vals {
|
||||||
if err := file.SetCellValue(sheet, col+r, val); err != nil {
|
if err := file.SetCellValue(sheet, col+r, val); err != nil {
|
||||||
@@ -198,9 +346,9 @@ func setMarketingExportRows(file *excelize.File, sheet string, items []dto.Marke
|
|||||||
week = strconv.Itoa(*prod.Week)
|
week = strconv.Itoa(*prod.Week)
|
||||||
}
|
}
|
||||||
|
|
||||||
satuan := "-"
|
gudang := "-"
|
||||||
if prod.ConvertionUnit != nil && strings.TrimSpace(*prod.ConvertionUnit) != "" {
|
if prod.ProductWarehouse != nil {
|
||||||
satuan = *prod.ConvertionUnit
|
gudang = safeMarketingExportText(prod.ProductWarehouse.Warehouse.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := file.SetCellValue(sheet, "A"+r, soNumber); err != nil {
|
if err := file.SetCellValue(sheet, "A"+r, soNumber); err != nil {
|
||||||
@@ -215,46 +363,49 @@ func setMarketingExportRows(file *excelize.File, sheet string, items []dto.Marke
|
|||||||
if err := file.SetCellValue(sheet, "D"+r, customer); err != nil {
|
if err := file.SetCellValue(sheet, "D"+r, customer); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := file.SetCellValue(sheet, "E"+r, safeMarketingExportText(prod.MarketingType)); err != nil {
|
if err := file.SetCellValue(sheet, "E"+r, salesPerson); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := file.SetCellValue(sheet, "F"+r, productName); err != nil {
|
if err := file.SetCellValue(sheet, "F"+r, safeMarketingExportText(prod.MarketingType)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := file.SetCellValue(sheet, "G"+r, week); err != nil {
|
if err := file.SetCellValue(sheet, "G"+r, productName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := file.SetCellValue(sheet, "H"+r, prod.Qty); err != nil {
|
if err := file.SetCellValue(sheet, "H"+r, gudang); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := file.SetCellValue(sheet, "I"+r, satuan); err != nil {
|
if err := file.SetCellValue(sheet, "I"+r, week); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, "J"+r, prod.Qty); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if prod.TotalPeti != nil {
|
if prod.TotalPeti != nil {
|
||||||
if err := file.SetCellValue(sheet, "J"+r, *prod.TotalPeti); err != nil {
|
if err := file.SetCellValue(sheet, "K"+r, *prod.TotalPeti); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := file.SetCellValue(sheet, "J"+r, "-"); err != nil {
|
if err := file.SetCellValue(sheet, "K"+r, "-"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := file.SetCellValue(sheet, "K"+r, prod.AvgWeight); err != nil {
|
if err := file.SetCellValue(sheet, "L"+r, prod.AvgWeight); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := file.SetCellValue(sheet, "L"+r, prod.TotalWeight); err != nil {
|
if err := file.SetCellValue(sheet, "M"+r, prod.TotalWeight); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := file.SetCellValue(sheet, "M"+r, prod.UnitPrice); err != nil {
|
if err := file.SetCellValue(sheet, "N"+r, prod.UnitPrice); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := file.SetCellValue(sheet, "N"+r, prod.TotalPrice); err != nil {
|
if err := file.SetCellValue(sheet, "O"+r, prod.TotalPrice); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := file.SetCellValue(sheet, "O"+r, grandTotal); err != nil {
|
if err := file.SetCellValue(sheet, "P"+r, grandTotal); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := file.SetCellValue(sheet, "P"+r, notes); err != nil {
|
if err := file.SetCellValue(sheet, "Q"+r, notes); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -276,7 +427,7 @@ func setMarketingExportRows(file *excelize.File, sheet string, items []dto.Marke
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := file.SetCellStyle(sheet, "A2", "P"+lastRowStr, dataStyle); err != nil {
|
if err := file.SetCellStyle(sheet, "A2", "Q"+lastRowStr, dataStyle); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,7 +438,7 @@ func setMarketingExportRows(file *excelize.File, sheet string, items []dto.Marke
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := file.SetCellStyle(sheet, "K2", "O"+lastRowStr, numberStyle); err != nil {
|
if err := file.SetCellStyle(sheet, "L2", "P"+lastRowStr, numberStyle); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +449,7 @@ func setMarketingExportRows(file *excelize.File, sheet string, items []dto.Marke
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, col := range []string{"G", "H", "J"} {
|
for _, col := range []string{"I", "J", "K"} {
|
||||||
if err := file.SetCellStyle(sheet, col+"2", col+lastRowStr, centerStyle); err != nil {
|
if err := file.SetCellStyle(sheet, col+"2", col+lastRowStr, centerStyle); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -327,16 +478,23 @@ func formatMarketingExportStatus(item dto.MarketingListDTO) string {
|
|||||||
return safeMarketingExportText(item.LatestApproval.StepName)
|
return safeMarketingExportText(item.LatestApproval.StepName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func sumMarketingGrandTotal(items []dto.DeliveryMarketingProductDTO) float64 {
|
func sumMarketingGrandTotal(items []dto.DeliveryMarketingProductDTO) float64 {
|
||||||
total := 0.0
|
total := 0.0
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
total += item.TotalPrice
|
total += item.TotalPrice
|
||||||
}
|
}
|
||||||
|
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sumDeliveryGrandTotal(groups []dto.DeliveryGroupDTO) float64 {
|
||||||
|
total := 0.0
|
||||||
|
for _, g := range groups {
|
||||||
|
for _, d := range g.Deliveries {
|
||||||
|
total += d.TotalPrice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
func safeMarketingExportText(value string) string {
|
func safeMarketingExportText(value string) string {
|
||||||
trimmed := strings.TrimSpace(value)
|
trimmed := strings.TrimSpace(value)
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ type MarketingListDTO struct {
|
|||||||
Customer customerDTO.CustomerRelationDTO `json:"customer"`
|
Customer customerDTO.CustomerRelationDTO `json:"customer"`
|
||||||
SalesPerson userDTO.UserRelationDTO `json:"sales_person"`
|
SalesPerson userDTO.UserRelationDTO `json:"sales_person"`
|
||||||
SoDocs string `json:"so_docs"`
|
SoDocs string `json:"so_docs"`
|
||||||
|
GrandTotalSO float64 `json:"grand_total_so"`
|
||||||
|
GrandTotalDO float64 `json:"grand_total_do"`
|
||||||
SalesOrder []DeliveryMarketingProductDTO `json:"sales_order"`
|
SalesOrder []DeliveryMarketingProductDTO `json:"sales_order"`
|
||||||
DeliveryOrder []DeliveryGroupDTO `json:"delivery_order"`
|
DeliveryOrder []DeliveryGroupDTO `json:"delivery_order"`
|
||||||
CreatedUser userDTO.UserRelationDTO `json:"created_user"`
|
CreatedUser userDTO.UserRelationDTO `json:"created_user"`
|
||||||
@@ -198,11 +200,18 @@ func ToMarketingListDTO(marketing *entity.Marketing, deliveryProducts []entity.M
|
|||||||
salesOrderProducts[i] = ToDeliveryMarketingProductDTO(product, marketing.MarketingType)
|
salesOrderProducts[i] = ToDeliveryMarketingProductDTO(product, marketing.MarketingType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var grandTotalSO float64
|
||||||
|
for _, p := range marketing.Products {
|
||||||
|
grandTotalSO += p.TotalPrice
|
||||||
|
}
|
||||||
|
|
||||||
return MarketingListDTO{
|
return MarketingListDTO{
|
||||||
MarketingRelationDTO: ToMarketingRelationDTO(marketing),
|
MarketingRelationDTO: ToMarketingRelationDTO(marketing),
|
||||||
Customer: customer,
|
Customer: customer,
|
||||||
SalesPerson: salesPerson,
|
SalesPerson: salesPerson,
|
||||||
SoDocs: marketing.SoDocs,
|
SoDocs: marketing.SoDocs,
|
||||||
|
GrandTotalSO: grandTotalSO,
|
||||||
|
GrandTotalDO: marketing.GrandTotal,
|
||||||
SalesOrder: salesOrderProducts,
|
SalesOrder: salesOrderProducts,
|
||||||
DeliveryOrder: extractDeliveryGroupsFromProducts(marketing),
|
DeliveryOrder: extractDeliveryGroupsFromProducts(marketing),
|
||||||
CreatedUser: createdUser,
|
CreatedUser: createdUser,
|
||||||
|
|||||||
@@ -287,6 +287,16 @@ func (s deliveryOrdersService) GetAll(c *fiber.Ctx, params *validation.DeliveryO
|
|||||||
db = db.Where("marketings.customer_id = ?", params.CustomerId)
|
db = db.Where("marketings.customer_id = ?", params.CustomerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if params.WarehouseID != 0 {
|
||||||
|
db = db.Where(`EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM marketing_products mp
|
||||||
|
JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id
|
||||||
|
WHERE mp.marketing_id = marketings.id
|
||||||
|
AND pw.warehouse_id = ?
|
||||||
|
)`, params.WarehouseID)
|
||||||
|
}
|
||||||
|
|
||||||
db = s.applyMarketingProjectFlockFilter(c.Context(), db, params.ProjectFlockID, params.ProjectFlockKandangID)
|
db = s.applyMarketingProjectFlockFilter(c.Context(), db, params.ProjectFlockID, params.ProjectFlockKandangID)
|
||||||
db = s.applyMarketingSearchFilter(c.Context(), db, params.Search)
|
db = s.applyMarketingSearchFilter(c.Context(), db, params.Search)
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ type DeliveryOrderQuery struct {
|
|||||||
MarketingId uint `query:"marketing_id" validate:"omitempty,gt=0"`
|
MarketingId uint `query:"marketing_id" validate:"omitempty,gt=0"`
|
||||||
ProjectFlockID uint `query:"project_flock_id" validate:"omitempty,gt=0"`
|
ProjectFlockID uint `query:"project_flock_id" validate:"omitempty,gt=0"`
|
||||||
ProjectFlockKandangID uint `query:"project_flock_kandang_id" validate:"omitempty,gt=0"`
|
ProjectFlockKandangID uint `query:"project_flock_kandang_id" validate:"omitempty,gt=0"`
|
||||||
|
WarehouseID uint `query:"warehouse_id" validate:"omitempty,gt=0"`
|
||||||
SortBy string `query:"sort_by" validate:"omitempty,oneof=so_number so_date status customer grand_total created_at"`
|
SortBy string `query:"sort_by" validate:"omitempty,oneof=so_number so_date status customer grand_total created_at"`
|
||||||
SortOrder string `query:"sort_order" validate:"omitempty,oneof=asc desc"`
|
SortOrder string `query:"sort_order" validate:"omitempty,oneof=asc desc"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,6 +172,23 @@ func (u *ChickinController) DeleteOne(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *ChickinController) UpdateChickInDate(c *fiber.Ctx) error {
|
||||||
|
req := new(validation.UpdateChickInDate)
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := u.ChickinService.UpdateChickInDate(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).JSON(response.Common{
|
||||||
|
Code: fiber.StatusOK,
|
||||||
|
Status: "success",
|
||||||
|
Message: "Chick in date berhasil diperbarui",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (u *ChickinController) Approval(c *fiber.Ctx) error {
|
func (u *ChickinController) Approval(c *fiber.Ctx) error {
|
||||||
req := new(validation.Approve)
|
req := new(validation.Approve)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package repository
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
@@ -18,6 +19,7 @@ type ProjectChickinRepository interface {
|
|||||||
GetTotalChickinQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error)
|
GetTotalChickinQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error)
|
||||||
GetByProjectFlockKandangIDForUpdate(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error)
|
GetByProjectFlockKandangIDForUpdate(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error)
|
||||||
UpdateUsageFields(ctx context.Context, tx *gorm.DB, chickinID uint, usageQty, pendingUsageQty float64) error
|
UpdateUsageFields(ctx context.Context, tx *gorm.DB, chickinID uint, usageQty, pendingUsageQty float64) error
|
||||||
|
UpdateChickInDateByProjectFlockKandangID(ctx context.Context, tx *gorm.DB, pfkID uint, newDate time.Time) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChickinRepositoryImpl struct {
|
type ChickinRepositoryImpl struct {
|
||||||
@@ -134,3 +136,10 @@ func (r *ChickinRepositoryImpl) UpdateUsageFields(ctx context.Context, tx *gorm.
|
|||||||
"pending_usage_qty": pendingUsageQty,
|
"pending_usage_qty": pendingUsageQty,
|
||||||
}).Error
|
}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ChickinRepositoryImpl) UpdateChickInDateByProjectFlockKandangID(ctx context.Context, tx *gorm.DB, pfkID uint, newDate time.Time) error {
|
||||||
|
return tx.WithContext(ctx).
|
||||||
|
Model(&entity.ProjectChickin{}).
|
||||||
|
Where("project_flock_kandang_id = ? AND deleted_at IS NULL", pfkID).
|
||||||
|
Update("chick_in_date", newDate).Error
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ func ChickinRoutes(v1 fiber.Router, u user.UserService, s chickin.ChickinService
|
|||||||
|
|
||||||
route.Get("/", m.RequirePermissions(m.P_ChickinsGetAll), ctrl.GetAll)
|
route.Get("/", m.RequirePermissions(m.P_ChickinsGetAll), ctrl.GetAll)
|
||||||
route.Post("/", m.RequirePermissions(m.P_ChickinsCreateOne), ctrl.CreateOne)
|
route.Post("/", m.RequirePermissions(m.P_ChickinsCreateOne), ctrl.CreateOne)
|
||||||
route.Get("/:id",m.RequirePermissions(m.P_ChickinsGetOne), ctrl.GetOne)
|
route.Patch("/chick-in-date", m.RequirePermissions(m.P_ChickinsCreateOne), ctrl.UpdateChickInDate)
|
||||||
|
route.Get("/:id", m.RequirePermissions(m.P_ChickinsGetOne), ctrl.GetOne)
|
||||||
// route.Patch("/:id", ctrl.UpdateOne)
|
// route.Patch("/:id", ctrl.UpdateOne)
|
||||||
route.Delete("/:id", ctrl.DeleteOne)
|
route.Delete("/:id", ctrl.DeleteOne)
|
||||||
route.Post("/approvals",m.RequirePermissions(m.P_ChickinsApproval), ctrl.Approval)
|
route.Post("/approvals", m.RequirePermissions(m.P_ChickinsApproval), ctrl.Approval)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ type ChickinService interface {
|
|||||||
DeleteOne(ctx *fiber.Ctx, id uint) error
|
DeleteOne(ctx *fiber.Ctx, id uint) error
|
||||||
Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.ProjectChickin, error)
|
Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.ProjectChickin, error)
|
||||||
EnsureChickInExists(ctx context.Context, projectFlockKandangID uint) error
|
EnsureChickInExists(ctx context.Context, projectFlockKandangID uint) error
|
||||||
|
UpdateChickInDate(ctx *fiber.Ctx, req *validation.UpdateChickInDate) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type chickinService struct {
|
type chickinService struct {
|
||||||
@@ -2110,3 +2111,38 @@ func (s chickinService) EnsureChickInExists(ctx context.Context, projectFlockKan
|
|||||||
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Chick in project flock belum disetujui sehingga belum dapat membuat recording")
|
return fiber.NewError(fiber.StatusBadRequest, "Chick in project flock belum disetujui sehingga belum dapat membuat recording")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s chickinService) UpdateChickInDate(ctx *fiber.Ctx, req *validation.UpdateChickInDate) error {
|
||||||
|
if err := s.Validate.Struct(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newDate, err := time.Parse("2006-01-02", req.ChickInDate)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Format tanggal tidak valid, gunakan YYYY-MM-DD")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.ProjectflockKandangRepo.GetByID(ctx.Context(), req.ProjectFlockKandangId)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, "Project flock kandang tidak ditemukan")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Repository.DB().WithContext(ctx.Context()).Transaction(func(tx *gorm.DB) error {
|
||||||
|
if err := s.Repository.UpdateChickInDateByProjectFlockKandangID(ctx.Context(), tx, req.ProjectFlockKandangId, newDate); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.Exec(`
|
||||||
|
UPDATE recordings
|
||||||
|
SET day = GREATEST(0, (record_datetime::date - ?::date)::int),
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE project_flock_kandangs_id = ?
|
||||||
|
AND deleted_at IS NULL
|
||||||
|
`, req.ChickInDate, req.ProjectFlockKandangId).Error
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.invalidateDepreciationSnapshots(ctx.Context(), nil, []uint{req.ProjectFlockKandangId}, newDate)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,3 +27,8 @@ type Approve struct {
|
|||||||
ApprovableIds []uint `json:"approvable_ids" validate:"required_strict,min=1,dive,gt=0"`
|
ApprovableIds []uint `json:"approvable_ids" validate:"required_strict,min=1,dive,gt=0"`
|
||||||
Notes *string `json:"notes,omitempty" validate:"omitempty,max=500"`
|
Notes *string `json:"notes,omitempty" validate:"omitempty,max=500"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateChickInDate struct {
|
||||||
|
ProjectFlockKandangId uint `json:"project_flock_kandang_id" validate:"required,gt=0"`
|
||||||
|
ChickInDate string `json:"chick_in_date" validate:"required,datetime=2006-01-02"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/common/exportprogress"
|
"gitlab.com/mbugroup/lti-api.git/internal/common/exportprogress"
|
||||||
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/dto"
|
"gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/dto"
|
||||||
service "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/services"
|
service "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/services"
|
||||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/validations"
|
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/validations"
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/response"
|
"gitlab.com/mbugroup/lti-api.git/internal/response"
|
||||||
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
@@ -75,6 +77,43 @@ func (u *RecordingController) GetAll(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
listDTO := dto.ToRecordingListDTOs(result)
|
listDTO := dto.ToRecordingListDTOs(result)
|
||||||
|
|
||||||
|
recordingIDs := make([]uint, 0, len(result))
|
||||||
|
for i := range result {
|
||||||
|
if result[i].Id != 0 {
|
||||||
|
recordingIDs = append(recordingIDs, result[i].Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(recordingIDs) > 0 {
|
||||||
|
eggs, err := u.RecordingService.GetEggsWithFlagsByRecordingIDs(c.Context(), recordingIDs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
eggByRecording := make(map[uint][]entity.RecordingEgg, len(recordingIDs))
|
||||||
|
for _, egg := range eggs {
|
||||||
|
eggByRecording[egg.RecordingId] = append(eggByRecording[egg.RecordingId], egg)
|
||||||
|
}
|
||||||
|
for i := range listDTO {
|
||||||
|
id := listDTO[i].Id
|
||||||
|
if eggList, ok := eggByRecording[id]; ok {
|
||||||
|
breakdown := make(map[string]dto.EggExportBreakdownDTO)
|
||||||
|
for _, egg := range eggList {
|
||||||
|
flagName := eggTypeFromProductName(egg.ProductWarehouse.Product.Name)
|
||||||
|
if flagName == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
entry := breakdown[flagName]
|
||||||
|
entry.Qty += egg.Qty
|
||||||
|
if egg.Weight != nil {
|
||||||
|
entry.Kg += *egg.Weight
|
||||||
|
}
|
||||||
|
breakdown[flagName] = entry
|
||||||
|
}
|
||||||
|
listDTO[i].EggExportBreakdown = breakdown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if strings.EqualFold(exportType, "excel") {
|
if strings.EqualFold(exportType, "excel") {
|
||||||
return exportRecordingListExcel(c, listDTO)
|
return exportRecordingListExcel(c, listDTO)
|
||||||
}
|
}
|
||||||
@@ -94,6 +133,33 @@ func (u *RecordingController) GetAll(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eggTypeFromProductName maps product name to egg type flag name by keyword matching.
|
||||||
|
// Falls back to empty string if no keyword matches.
|
||||||
|
func eggTypeFromProductName(name string) string {
|
||||||
|
normalized := strings.ToLower(strings.TrimSpace(name))
|
||||||
|
if normalized == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
// Ordered longest-first to prefer "papacal" over partial match of "pacal", etc.
|
||||||
|
keywords := []struct {
|
||||||
|
keyword string
|
||||||
|
flag string
|
||||||
|
}{
|
||||||
|
{"papacal", string(utils.FlagTelurPapacal)},
|
||||||
|
{"jumbo", string(utils.FlagTelurJumbo)},
|
||||||
|
{"retak", string(utils.FlagTelurRetak)},
|
||||||
|
{"putih", string(utils.FlagTelurPutih)},
|
||||||
|
{"pecah", string(utils.FlagTelurPecah)},
|
||||||
|
{"utuh", string(utils.FlagTelurUtuh)},
|
||||||
|
}
|
||||||
|
for _, k := range keywords {
|
||||||
|
if strings.Contains(normalized, k.keyword) {
|
||||||
|
return k.flag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (u *RecordingController) GetOne(c *fiber.Ctx) error {
|
func (u *RecordingController) GetOne(c *fiber.Ctx) error {
|
||||||
param := c.Params("id")
|
param := c.Params("id")
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/dto"
|
"gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/dto"
|
||||||
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/xuri/excelize/v2"
|
"github.com/xuri/excelize/v2"
|
||||||
@@ -79,6 +80,18 @@ func setRecordingExportColumns(file *excelize.File, sheet string) error {
|
|||||||
"AB": 18,
|
"AB": 18,
|
||||||
"AC": 24,
|
"AC": 24,
|
||||||
"AD": 18,
|
"AD": 18,
|
||||||
|
"AE": 12,
|
||||||
|
"AF": 10,
|
||||||
|
"AG": 12,
|
||||||
|
"AH": 10,
|
||||||
|
"AI": 12,
|
||||||
|
"AJ": 10,
|
||||||
|
"AK": 12,
|
||||||
|
"AL": 10,
|
||||||
|
"AM": 12,
|
||||||
|
"AN": 10,
|
||||||
|
"AO": 12,
|
||||||
|
"AP": 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
for col, width := range columnWidths {
|
for col, width := range columnWidths {
|
||||||
@@ -208,6 +221,31 @@ func setRecordingExportHeaders(file *excelize.File, sheet string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
eggTypes := []struct {
|
||||||
|
col1, col2, label string
|
||||||
|
}{
|
||||||
|
{"AE", "AF", "Telur Utuh"},
|
||||||
|
{"AG", "AH", "Telur Pecah"},
|
||||||
|
{"AI", "AJ", "Telur Putih"},
|
||||||
|
{"AK", "AL", "Telur Retak"},
|
||||||
|
{"AM", "AN", "Telur Papacal"},
|
||||||
|
{"AO", "AP", "Telur Jumbo"},
|
||||||
|
}
|
||||||
|
for _, et := range eggTypes {
|
||||||
|
if err := file.MergeCell(sheet, et.col1+"1", et.col2+"1"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, et.col1+"1", et.label); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, et.col1+"2", "Butir"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := file.SetCellValue(sheet, et.col2+"2", "Kg"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
headerStyle, err := file.NewStyle(&excelize.Style{
|
headerStyle, err := file.NewStyle(&excelize.Style{
|
||||||
Font: &excelize.Font{
|
Font: &excelize.Font{
|
||||||
Bold: true,
|
Bold: true,
|
||||||
@@ -234,7 +272,7 @@ func setRecordingExportHeaders(file *excelize.File, sheet string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return file.SetCellStyle(sheet, "A1", "AD2", headerStyle)
|
return file.SetCellStyle(sheet, "A1", "AP2", headerStyle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setRecordingExportRows(file *excelize.File, sheet string, items []dto.RecordingListDTO) error {
|
func setRecordingExportRows(file *excelize.File, sheet string, items []dto.RecordingListDTO) error {
|
||||||
@@ -245,7 +283,8 @@ func setRecordingExportRows(file *excelize.File, sheet string, items []dto.Recor
|
|||||||
columns := []string{
|
columns := []string{
|
||||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
|
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
|
||||||
"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "AB",
|
"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "AB",
|
||||||
"AC", "AD",
|
"AC", "AD", "AE", "AF", "AG", "AH", "AI", "AJ", "AK", "AL", "AM", "AN",
|
||||||
|
"AO", "AP",
|
||||||
}
|
}
|
||||||
|
|
||||||
currentRow := 3
|
currentRow := 3
|
||||||
@@ -293,14 +332,14 @@ func setRecordingExportRows(file *excelize.File, sheet string, items []dto.Recor
|
|||||||
// Expand recordings into one row per sapronak
|
// Expand recordings into one row per sapronak
|
||||||
type sapronakRow struct {
|
type sapronakRow struct {
|
||||||
name string
|
name string
|
||||||
input string
|
input interface{} // float64 for numeric, string "-" for placeholder
|
||||||
}
|
}
|
||||||
sapronaks := make([]sapronakRow, 0)
|
sapronaks := make([]sapronakRow, 0)
|
||||||
if len(item.FeedUsage) > 0 {
|
if len(item.FeedUsage) > 0 {
|
||||||
for _, fu := range item.FeedUsage {
|
for _, fu := range item.FeedUsage {
|
||||||
sapronaks = append(sapronaks, sapronakRow{
|
sapronaks = append(sapronaks, sapronakRow{
|
||||||
name: safeExportText(fu.ProductName),
|
name: safeExportText(fu.ProductName),
|
||||||
input: formatNumberID(fu.UsageAmount+fu.PendingQty, 2, true),
|
input: fu.UsageAmount + fu.PendingQty,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -311,37 +350,66 @@ func setRecordingExportRows(file *excelize.File, sheet string, items []dto.Recor
|
|||||||
|
|
||||||
for sIdx, s := range sapronaks {
|
for sIdx, s := range sapronaks {
|
||||||
if sIdx == 0 {
|
if sIdx == 0 {
|
||||||
|
eggQty := func(flagName string) int {
|
||||||
|
if item.EggExportBreakdown != nil {
|
||||||
|
if bd, ok := item.EggExportBreakdown[flagName]; ok {
|
||||||
|
return bd.Qty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
eggKg := func(flagName string) float64 {
|
||||||
|
if item.EggExportBreakdown != nil {
|
||||||
|
if bd, ok := item.EggExportBreakdown[flagName]; ok {
|
||||||
|
return bd.Kg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
rowValues := []interface{}{
|
rowValues := []interface{}{
|
||||||
i + 1, // A
|
i + 1, // A
|
||||||
locationName, // B
|
locationName, // B
|
||||||
safeExportText(item.ProjectFlock.FlockName), // C
|
safeExportText(item.ProjectFlock.FlockName), // C
|
||||||
kandangName, // D
|
kandangName, // D
|
||||||
item.ProjectFlock.Period, // E
|
item.ProjectFlock.Period, // E
|
||||||
formatCategoryLabel(item.ProjectFlock.ProjectFlockCategory), // F
|
formatCategoryLabel(item.ProjectFlock.ProjectFlockCategory), // F
|
||||||
formatAgeLabel(item), // G
|
formatAgeLabel(item), // G
|
||||||
formatDateIndonesian(item.RecordDatetime), // H
|
formatDateIndonesian(item.RecordDatetime), // H
|
||||||
formatNumberID(item.ProjectFlock.TotalChickQty, 0, false), // I
|
item.ProjectFlock.TotalChickQty, // I
|
||||||
formatNumberID(item.FcrValue, 2, true), // J
|
item.FcrValue, // J
|
||||||
formatNumberID(fcrStd, 2, true), // K
|
fcrStd, // K
|
||||||
formatNumberID(item.FeedIntake, 2, true), // L
|
item.FeedIntake, // L
|
||||||
formatNumberID(feedIntakeStd, 2, true), // M
|
feedIntakeStd, // M
|
||||||
formatPercentID(item.CumDepletionRate, 2), // N
|
item.CumDepletionRate, // N
|
||||||
formatPercentID(maxDepletionStd, 2), // O
|
maxDepletionStd, // O
|
||||||
formatNumberID(item.TotalDepletionQty, 2, true), // P
|
item.TotalDepletionQty, // P
|
||||||
formatNumberID(item.EggMass, 2, true), // Q
|
item.EggMass, // Q
|
||||||
formatNumberID(eggMassStd, 2, true), // R
|
eggMassStd, // R
|
||||||
formatNumberID(item.EggWeight, 2, true), // S
|
item.EggWeight, // S
|
||||||
formatNumberID(eggWeightStd, 2, true), // T
|
eggWeightStd, // T
|
||||||
formatPercentID(item.HenDay, 2), // U
|
item.HenDay, // U
|
||||||
formatPercentID(henDayStd, 2), // V
|
henDayStd, // V
|
||||||
formatPercentID(item.HenHouse, 2), // W
|
item.HenHouse, // W
|
||||||
formatPercentID(henHouseStd, 2), // X
|
henHouseStd, // X
|
||||||
formatApprovalStatus(item), // Y
|
formatApprovalStatus(item), // Y
|
||||||
safeExportText(pointerString(item.Approval.Notes)), // Z
|
safeExportText(pointerString(item.Approval.Notes)), // Z
|
||||||
createdBy, // AA
|
createdBy, // AA
|
||||||
formatDateIndonesian(item.CreatedAt), // AB
|
formatDateIndonesian(item.CreatedAt), // AB
|
||||||
s.name, // AC
|
s.name, // AC
|
||||||
s.input, // AD
|
s.input, // AD
|
||||||
|
eggQty(string(utils.FlagTelurUtuh)), // AE
|
||||||
|
eggKg(string(utils.FlagTelurUtuh)), // AF
|
||||||
|
eggQty(string(utils.FlagTelurPecah)), // AG
|
||||||
|
eggKg(string(utils.FlagTelurPecah)), // AH
|
||||||
|
eggQty(string(utils.FlagTelurPutih)), // AI
|
||||||
|
eggKg(string(utils.FlagTelurPutih)), // AJ
|
||||||
|
eggQty(string(utils.FlagTelurRetak)), // AK
|
||||||
|
eggKg(string(utils.FlagTelurRetak)), // AL
|
||||||
|
eggQty(string(utils.FlagTelurPapacal)), // AM
|
||||||
|
eggKg(string(utils.FlagTelurPapacal)), // AN
|
||||||
|
eggQty(string(utils.FlagTelurJumbo)), // AO
|
||||||
|
eggKg(string(utils.FlagTelurJumbo)), // AP
|
||||||
}
|
}
|
||||||
|
|
||||||
for idx, col := range columns {
|
for idx, col := range columns {
|
||||||
@@ -379,7 +447,7 @@ func setRecordingExportRows(file *excelize.File, sheet string, items []dto.Recor
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := file.SetCellStyle(sheet, "A3", fmt.Sprintf("AD%d", lastRow), dataCenterStyle); err != nil {
|
if err := file.SetCellStyle(sheet, "A3", fmt.Sprintf("AP%d", lastRow), dataCenterStyle); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,6 +513,7 @@ func setRecordingExportRows(file *excelize.File, sheet string, items []dto.Recor
|
|||||||
|
|
||||||
mergeCols := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
|
mergeCols := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
|
||||||
"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "AB",
|
"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "AB",
|
||||||
|
"AE", "AF", "AG", "AH", "AI", "AJ", "AK", "AL", "AM", "AN", "AO", "AP",
|
||||||
}
|
}
|
||||||
for _, rng := range itemRanges {
|
for _, rng := range itemRanges {
|
||||||
if rng.end > rng.start {
|
if rng.end > rng.start {
|
||||||
@@ -454,6 +523,53 @@ func setRecordingExportRows(file *excelize.File, sheet string, items []dto.Recor
|
|||||||
}
|
}
|
||||||
file.SetCellStyle(sheet, fmt.Sprintf("AC%d", rng.end), fmt.Sprintf("AC%d", rng.end), borderBottomLeftStyle)
|
file.SetCellStyle(sheet, fmt.Sprintf("AC%d", rng.end), fmt.Sprintf("AC%d", rng.end), borderBottomLeftStyle)
|
||||||
file.SetCellStyle(sheet, fmt.Sprintf("AD%d", rng.end), fmt.Sprintf("AD%d", rng.end), borderBottomCenterStyle)
|
file.SetCellStyle(sheet, fmt.Sprintf("AD%d", rng.end), fmt.Sprintf("AD%d", rng.end), borderBottomCenterStyle)
|
||||||
|
// Egg columns use center + thick bottom border
|
||||||
|
for _, col := range []string{"AE", "AF", "AG", "AH", "AI", "AJ", "AK", "AL", "AM", "AN", "AO", "AP"} {
|
||||||
|
file.SetCellStyle(sheet, fmt.Sprintf("%s%d", col, rng.end), fmt.Sprintf("%s%d", col, rng.end), borderBottomCenterStyle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
numFmtInt := "0"
|
||||||
|
numberIntStyle, err := file.NewStyle(&excelize.Style{
|
||||||
|
CustomNumFmt: &numFmtInt,
|
||||||
|
Alignment: &excelize.Alignment{Horizontal: "center", Vertical: "center", WrapText: true},
|
||||||
|
Border: []excelize.Border{
|
||||||
|
{Type: "left", Color: "E6E6E6", Style: 1},
|
||||||
|
{Type: "top", Color: "E6E6E6", Style: 1},
|
||||||
|
{Type: "bottom", Color: "E6E6E6", Style: 1},
|
||||||
|
{Type: "right", Color: "E6E6E6", Style: 1},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
numFmtFloat := "0.00"
|
||||||
|
numberFloatStyle, err := file.NewStyle(&excelize.Style{
|
||||||
|
CustomNumFmt: &numFmtFloat,
|
||||||
|
Alignment: &excelize.Alignment{Horizontal: "center", Vertical: "center", WrapText: true},
|
||||||
|
Border: []excelize.Border{
|
||||||
|
{Type: "left", Color: "E6E6E6", Style: 1},
|
||||||
|
{Type: "top", Color: "E6E6E6", Style: 1},
|
||||||
|
{Type: "bottom", Color: "E6E6E6", Style: 1},
|
||||||
|
{Type: "right", Color: "E6E6E6", Style: 1},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
intCols := []string{"E", "I", "AE", "AG", "AI", "AK", "AM", "AO"}
|
||||||
|
for _, col := range intCols {
|
||||||
|
if err := file.SetCellStyle(sheet, col+"3", fmt.Sprintf("%s%d", col, lastRow), numberIntStyle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
floatCols := []string{"J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "AD", "AF", "AH", "AJ", "AL", "AN", "AP"}
|
||||||
|
for _, col := range floatCols {
|
||||||
|
if err := file.SetCellStyle(sheet, col+"3", fmt.Sprintf("%s%d", col, lastRow), numberFloatStyle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -100,14 +100,20 @@ type RecordingFeedUsageDTO struct {
|
|||||||
PendingQty float64 `json:"pending_qty"`
|
PendingQty float64 `json:"pending_qty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EggExportBreakdownDTO struct {
|
||||||
|
Qty int `json:"qty"`
|
||||||
|
Kg float64 `json:"kg"`
|
||||||
|
}
|
||||||
|
|
||||||
type RecordingListDTO struct {
|
type RecordingListDTO struct {
|
||||||
RecordingRelationDTO
|
RecordingRelationDTO
|
||||||
CreatedUser *userDTO.UserRelationDTO `json:"created_user,omitempty"`
|
CreatedUser *userDTO.UserRelationDTO `json:"created_user,omitempty"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
Kandang *RecordingKandangDTO `json:"kandang,omitempty"`
|
Kandang *RecordingKandangDTO `json:"kandang,omitempty"`
|
||||||
Location *RecordingLocationDTO `json:"location,omitempty"`
|
Location *RecordingLocationDTO `json:"location,omitempty"`
|
||||||
FeedUsage []RecordingFeedUsageDTO `json:"feed_usage,omitempty"`
|
FeedUsage []RecordingFeedUsageDTO `json:"feed_usage,omitempty"`
|
||||||
|
EggExportBreakdown map[string]EggExportBreakdownDTO `json:"egg_breakdown,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RecordingDetailDTO struct {
|
type RecordingDetailDTO struct {
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ type RecordingRepository interface {
|
|||||||
UpdateEggTotalQty(tx *gorm.DB, eggID uint, totalQty float64) error
|
UpdateEggTotalQty(tx *gorm.DB, eggID uint, totalQty float64) error
|
||||||
UpdateEggWeight(tx *gorm.DB, eggID uint, weight *float64) error
|
UpdateEggWeight(tx *gorm.DB, eggID uint, weight *float64) error
|
||||||
GetRecordingEggByID(ctx context.Context, id uint, modifier func(*gorm.DB) *gorm.DB) (*entity.RecordingEgg, error)
|
GetRecordingEggByID(ctx context.Context, id uint, modifier func(*gorm.DB) *gorm.DB) (*entity.RecordingEgg, error)
|
||||||
|
GetEggsWithFlagsByRecordingIDs(ctx context.Context, recordingIDs []uint) ([]entity.RecordingEgg, error)
|
||||||
|
|
||||||
ExistsOnDate(ctx context.Context, projectFlockKandangId uint, recordTime time.Time) (bool, error)
|
ExistsOnDate(ctx context.Context, projectFlockKandangId uint, recordTime time.Time) (bool, error)
|
||||||
|
|
||||||
@@ -581,6 +582,22 @@ func (r *RecordingRepositoryImpl) GetRecordingEggByID(
|
|||||||
return &egg, nil
|
return &egg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RecordingRepositoryImpl) GetEggsWithFlagsByRecordingIDs(ctx context.Context, recordingIDs []uint) ([]entity.RecordingEgg, error) {
|
||||||
|
if len(recordingIDs) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var eggs []entity.RecordingEgg
|
||||||
|
err := r.DB().WithContext(ctx).
|
||||||
|
Preload("ProductWarehouse.Product").
|
||||||
|
Where("recording_eggs.recording_id IN ?", recordingIDs).
|
||||||
|
Find(&eggs).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return eggs, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *RecordingRepositoryImpl) ExistsOnDate(ctx context.Context, projectFlockKandangId uint, recordTime time.Time) (bool, error) {
|
func (r *RecordingRepositoryImpl) ExistsOnDate(ctx context.Context, projectFlockKandangId uint, recordTime time.Time) (bool, error) {
|
||||||
if projectFlockKandangId == 0 {
|
if projectFlockKandangId == 0 {
|
||||||
return false, nil
|
return false, nil
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ type RecordingService interface {
|
|||||||
DeleteOne(ctx *fiber.Ctx, id uint) error
|
DeleteOne(ctx *fiber.Ctx, id uint) error
|
||||||
Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.Recording, error)
|
Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.Recording, error)
|
||||||
GetProgressRows(ctx *fiber.Ctx, query *exportprogress.Query) ([]exportprogress.Row, error)
|
GetProgressRows(ctx *fiber.Ctx, query *exportprogress.Query) ([]exportprogress.Row, error)
|
||||||
|
GetEggsWithFlagsByRecordingIDs(ctx context.Context, recordingIDs []uint) ([]entity.RecordingEgg, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type recordingService struct {
|
type recordingService struct {
|
||||||
@@ -259,6 +260,10 @@ func (s recordingService) GetProgressRows(c *fiber.Ctx, query *exportprogress.Qu
|
|||||||
return s.Repository.GetProgressRows(c.Context(), query.StartDate, query.EndDate, scope.IDs, scope.Restrict)
|
return s.Repository.GetProgressRows(c.Context(), query.StartDate, query.EndDate, scope.IDs, scope.Restrict)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s recordingService) GetEggsWithFlagsByRecordingIDs(ctx context.Context, recordingIDs []uint) ([]entity.RecordingEgg, error) {
|
||||||
|
return s.Repository.GetEggsWithFlagsByRecordingIDs(ctx, recordingIDs)
|
||||||
|
}
|
||||||
|
|
||||||
func (s recordingService) GetOne(c *fiber.Ctx, id uint) (*entity.Recording, error) {
|
func (s recordingService) GetOne(c *fiber.Ctx, id uint) (*entity.Recording, error) {
|
||||||
if err := m.EnsureRecordingAccess(c, s.Repository.DB(), id); err != nil {
|
if err := m.EnsureRecordingAccess(c, s.Repository.DB(), id); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
Reference in New Issue
Block a user