Implement the SKUs and the Inventory Movement Manager, to apply variability and functions for sales, restock co.

This commit is contained in:
Domonkos
2026-01-20 18:59:07 +01:00
parent e2868eade0
commit 407df07f7a
9 changed files with 210 additions and 0 deletions

View File

View 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);
}
}

View File

@@ -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; }
}

View File

@@ -0,0 +1,8 @@
package com.voyage.workspace.inventory;
public record InventoryMovementCreateRequest(
Long skuId,
int delta,
String reason,
String reference
) {}

View File

@@ -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);
}

View File

@@ -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; }
}

View File

@@ -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));
}
}

View File

@@ -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
) {}

View File

@@ -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);
}