Implement the SKUs and the Inventory Movement Manager, to apply variability and functions for sales, restock co.
This commit is contained in:
0
apps/workspace-api/Doc.txt
Normal file
0
apps/workspace-api/Doc.txt
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package com.voyage.workspace.inventory;
|
||||||
|
|
||||||
|
import com.voyage.workspace.products.SkuRepository;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/inventory")
|
||||||
|
public class InventoryController {
|
||||||
|
|
||||||
|
private final InventoryMovementRepository movementRepo;
|
||||||
|
private final SkuRepository skuRepo;
|
||||||
|
|
||||||
|
public InventoryController(InventoryMovementRepository movementRepo, SkuRepository skuRepo) {
|
||||||
|
this.movementRepo = movementRepo;
|
||||||
|
this.skuRepo = skuRepo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/movements")
|
||||||
|
public ResponseEntity<?> createMovement(@RequestBody InventoryMovementCreateRequest req) {
|
||||||
|
var skuOpt = skuRepo.findById(req.skuId());
|
||||||
|
if (skuOpt.isEmpty()) return ResponseEntity.badRequest().body("Unknown skuId: " + req.skuId());
|
||||||
|
|
||||||
|
InventoryMovement m = new InventoryMovement();
|
||||||
|
m.setSku(skuOpt.get());
|
||||||
|
m.setDelta(req.delta());
|
||||||
|
m.setReason(req.reason());
|
||||||
|
m.setReference(req.reference());
|
||||||
|
|
||||||
|
return ResponseEntity.ok(movementRepo.save(m));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/stock/{skuId}")
|
||||||
|
public int getCurrentStock(@PathVariable Long skuId) {
|
||||||
|
return movementRepo.currentStock(skuId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.voyage.workspace.inventory;
|
||||||
|
|
||||||
|
import com.voyage.workspace.products.Sku;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "inventory_movements")
|
||||||
|
public class InventoryMovement {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ManyToOne(optional = false, fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "sku_id", nullable = false)
|
||||||
|
private Sku sku;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private int delta;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String reason; // PRODUCTION, SALE, RETURN, ADJUSTMENT
|
||||||
|
|
||||||
|
private String reference;
|
||||||
|
|
||||||
|
@Column(nullable=false, updatable=false)
|
||||||
|
private Instant createdAt = Instant.now();
|
||||||
|
|
||||||
|
public InventoryMovement() {}
|
||||||
|
|
||||||
|
public Long getId() { return id; }
|
||||||
|
public Sku getSku() { return sku; }
|
||||||
|
public int getDelta() { return delta; }
|
||||||
|
public String getReason() { return reason; }
|
||||||
|
public String getReference() { return reference; }
|
||||||
|
public Instant getCreatedAt() { return createdAt; }
|
||||||
|
|
||||||
|
public void setSku(Sku sku) { this.sku = sku; }
|
||||||
|
public void setDelta(int delta) { this.delta = delta; }
|
||||||
|
public void setReason(String reason) { this.reason = reason; }
|
||||||
|
public void setReference(String reference) { this.reference = reference; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.voyage.workspace.inventory;
|
||||||
|
|
||||||
|
public record InventoryMovementCreateRequest(
|
||||||
|
Long skuId,
|
||||||
|
int delta,
|
||||||
|
String reason,
|
||||||
|
String reference
|
||||||
|
) {}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.voyage.workspace.inventory;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
public interface InventoryMovementRepository extends JpaRepository<InventoryMovement, Long> {
|
||||||
|
|
||||||
|
@Query("select coalesce(sum(m.delta), 0) from InventoryMovement m where m.sku.id = :skuId")
|
||||||
|
int currentStock(@Param("skuId") Long skuId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.voyage.workspace.products;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "skus")
|
||||||
|
public class Sku {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ManyToOne(optional = false, fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "product_id", nullable = false)
|
||||||
|
private Product product;
|
||||||
|
|
||||||
|
@Column(nullable = false, unique = true)
|
||||||
|
private String skuCode;
|
||||||
|
|
||||||
|
private String size;
|
||||||
|
private String color;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private BigDecimal price;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private boolean active = true;
|
||||||
|
|
||||||
|
public Sku() {}
|
||||||
|
|
||||||
|
public Long getId() { return id; }
|
||||||
|
public Product getProduct() { return product; }
|
||||||
|
public String getSkuCode() { return skuCode; }
|
||||||
|
public String getSize() { return size; }
|
||||||
|
public String getColor() { return color; }
|
||||||
|
public BigDecimal getPrice() { return price; }
|
||||||
|
public boolean isActive() { return active; }
|
||||||
|
|
||||||
|
public void setProduct(Product product) { this.product = product; }
|
||||||
|
public void setSkuCode(String skuCode) { this.skuCode = skuCode; }
|
||||||
|
public void setSize(String size) { this.size = size; }
|
||||||
|
public void setColor(String color) { this.color = color; }
|
||||||
|
public void setPrice(BigDecimal price) { this.price = price; }
|
||||||
|
public void setActive(boolean active) { this.active = active; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.voyage.workspace.products;
|
||||||
|
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/skus")
|
||||||
|
public class SkuController {
|
||||||
|
|
||||||
|
private final SkuRepository skuRepo;
|
||||||
|
private final ProductRepository productRepo;
|
||||||
|
|
||||||
|
public SkuController(SkuRepository skuRepo, ProductRepository productRepo) {
|
||||||
|
this.skuRepo = skuRepo;
|
||||||
|
this.productRepo = productRepo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<Sku> list(@RequestParam(required = false) Long productId) {
|
||||||
|
if (productId != null) return skuRepo.findByProductId(productId);
|
||||||
|
return skuRepo.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<?> create(@RequestBody SkuCreateRequest req) {
|
||||||
|
var productOpt = productRepo.findById(req.productId());
|
||||||
|
if (productOpt.isEmpty()) {
|
||||||
|
return ResponseEntity.badRequest().body("Unknown productId: " + req.productId());
|
||||||
|
}
|
||||||
|
|
||||||
|
Sku sku = new Sku();
|
||||||
|
sku.setProduct(productOpt.get());
|
||||||
|
sku.setSkuCode(req.skuCode());
|
||||||
|
sku.setSize(req.size());
|
||||||
|
sku.setColor(req.color());
|
||||||
|
sku.setPrice(req.price());
|
||||||
|
|
||||||
|
return ResponseEntity.ok(skuRepo.save(sku));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.voyage.workspace.products;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
public record SkuCreateRequest(
|
||||||
|
Long productId,
|
||||||
|
String skuCode,
|
||||||
|
String size,
|
||||||
|
String color,
|
||||||
|
BigDecimal price
|
||||||
|
) {}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.voyage.workspace.products;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface SkuRepository extends JpaRepository<Sku, Long> {
|
||||||
|
List<Sku> findByProductId(Long productId);
|
||||||
|
Optional<Sku> findBySkuCode(String skuCode);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user