syntax = "proto3";
package upl;
import "google/protobuf/empty.proto";

service Upl {
  // Create new UPL
  // Just from a procurement or inventory service!
  // No public API should be implemented!
  rpc CreateNew(UplNew) returns (UplObj);

  // Create new UPL in bulk
  // Used during closing a procurement process
  rpc CreateNewBulk(stream UplNew) returns (UplIds);

  // Get UPLs in bulk
  rpc GetBulk(BulkRequest) returns (stream UplObj);

  // Get UPL by ID
  rpc GetById(ByIdRequest) returns (UplObj);

  // Get UPL by ID from archive
  rpc GetByIdArchive(ByIdRequest) returns (UplObj);

  // Get UPLs by Sku
  rpc GetBySku(BySkuRequest) returns (UplIds);

  // Get UPLs by ProductId
  rpc GetByProduct(ByProductRequest) returns (UplIds);

  // Get UPLs by Sku and Location
  rpc GetBySkuAndLocation(BySkuAndLocationRequest) returns (UplIds);

  // Get UPLs by Location
  rpc GetByLocation(ByLocationRequest) returns (UplIds);

  // Get stock info
  // rpc GetStockInfo(StockInfoRequest) returns (stream StockInfo);

  // Restore archived UPL to a given Stock
  // Should be two reason:
  //   1) Sold UPL is back for any reason
  //   2) Discarded(Missing) product is back
  // rpc RestoreToStock(e) returns (UplObj);

  // Modify best before date
  // This should not use at all
  // As this info should be correctly provided by
  // the procurement process
  // The only possible usage to correct a bestbefore date
  // error during inventory check.
  rpc SetBestBefore(SetBestBeforeRequest) returns (UplObj);

  // Split one UPL from BulkUpl
  rpc Split(SplitRequest) returns (UplObj);

  // Divide divisible Upl or OpenedUpl into a smaller Upl
  rpc Divide(DivideRequest) returns (UplObj);

  // Set depreciation
  // ID and comment
  rpc SetDepreciation(DepreciationRequest) returns (UplObj);
  // Remove depreciation
  // Remove the depreciated net retail price as well
  rpc RemoveDepreciation(DepreciationRemoveRequest) returns (UplObj);
  // Set depreciation net retail price
  // Only if there is depceriation ID already set
  rpc SetDepreciationPrice(DepreciationPriceRequest) returns (UplObj);

  // Try to remove deprecation price
  // if already depreciated
  rpc RemoveDeprecationPrice(RemoveDeprecationPriceRequest) returns (UplObj);

  // Put UPL to a Cart (Apply lock)
  rpc LockToCart(CartLockRequest) returns (UplObj);

  // Remove UPL from a Cart (Release lock)
  rpc ReleaseLockFromCart(CartUnlockRequest) returns (UplObj);

  // Checkout delivery UPLs to a given Stock(ID)
  // Close a cart (transform lock to location)
  rpc CloseCart(CloseCartRequest) returns (google.protobuf.Empty);

  // Put UPL to a Delivery (Apply lock)
  // rpc LockToDelivery(e) returns (UplObj);

  // Put UPL to an Inventory (Apply lock)
  // rpc LockToInventory(InventoryLockRequest) returns (UplObj);

  // Remove UPL from an Inventory (Release lock)
  // rpc ReleaseLockFromInventory(InventoryUnlockRequest) returns (UplObj);

  // Close an inventory (remove lock, but no location change)
  // rpc CloseInventory(CloseInventoryRequest) returns (google.protobuf.Empty);

  // Remove UPL from a Delivery (Release lock)
  // rpc ReleaseLockFromDelivery(e) returns (UplObj);

  // Close a delivery (transform lock to location)
  // rpc CloseDelivery(e) returns (e);

  // Set new price to UPLs by SKU
  //   set new price to Sku, BulkSku, OpenedSku and DerivedProduct
  //   if their location is not Cart(Y)
  rpc SetSkuPrice(SetSkuPriceRequest) returns (google.protobuf.Empty);

  // Set UPL to be divisible by SKU
  rpc SetSkuDivisible(SetSkuDivisibleRequest) returns (google.protobuf.Empty);

  // Open single SKU(x) -> OpenedSku(x)
  //   where x is the related amount
  rpc OpenUpl(OpenUplRequest) returns (UplObj);

  // Try to close opened SKU
  // only if amount == sku_amount
  // OpenedSku -> Sku
  rpc CloseUpl(CloseUplRequest) returns (UplObj);

  // Merge DerivedProduct back to its ancestor
  rpc MergeBack(MergeRequest) returns (google.protobuf.Empty);

  // Get UPL location info by SKU
  rpc GetLocationInfo(LocationInfoRequest) returns (LocationInfoResponse);

  // Get UPL location info by SKU bulk
  rpc GetLocationInfoBulk(LocationInfoBulkRequest)
      returns (stream LocationInfoResponse);

  rpc SetProductUnit(SetProductUnitRequest) returns (google.protobuf.Empty);
}

message ByProductRequest { uint32 product_id = 1; }

message SetProductUnitRequest {
  uint32 product_id = 1;
  string unit = 2;
}

message StockInfo {
  uint32 total = 1;
  uint32 healthy = 2;
  uint32 bulk = 3;
  uint32 opened = 4;
}

message LocationInfoResponse {
  uint32 sku = 1;
  map<uint32, StockInfo> stocks = 2;
}

message LocationInfoRequest { uint32 sku = 1; }

message LocationInfoBulkRequest { repeated uint32 sku = 1; }

message SetSkuPriceRequest {
  uint32 sku = 1;
  uint32 net_price = 2;
  string vat = 3;
  uint32 gross_price = 4;
}

message SetSkuDivisibleRequest {
  uint32 sku = 1;
  bool divisible = 2;
}

message OpenUplRequest { string upl_id = 1; }

message CloseUplRequest { string upl_id = 1; }

message MergeRequest {
  string upl_to_merge_back = 1;
  uint32 created_by = 2;
}

message BulkRequest { repeated string upl_ids = 1; }

message UplIds { repeated string upl_ids = 1; }

// New UPL object
message UplNew {
  // UPL ID
  string upl_id = 1;
  // Related product ID
  uint32 product_id = 2;
  // Related product unit
  string product_unit = 3;
  // Related SKU
  uint32 sku = 4;
  // UPL piece; if bulk, than bigger then 1
  uint32 piece = 5;
  // Related SKU divisible amount
  uint32 sku_divisible_amount = 6;
  bool sku_divisible = 7;
  uint32 sku_net_price = 8;
  string sku_vat = 9;
  uint32 sku_gross_price = 10;
  uint32 procurement_id = 11;
  // Related procurement net price for Sku (unopened)
  uint32 procurement_net_price_sku = 12;
  // Location will be Stock(ID)
  uint32 stock_id = 13;
  // Best before, if there is any
  string best_before = 14;
  // True if opened SKU
  // False if SKU or bulk SKU
  bool is_opened = 15;
  // UPL total net price for package
  // User who registered the UPL init data
  uint32 created_by = 16;
}

message ByIdRequest { string upl_id = 1; }

message BySkuRequest { uint32 sku = 1; }

// Possible UPL locations
enum LocationKind {
  Stock = 0;
  Cart = 1;
  Delivery = 2;
  Discard = 3;
}

message ByLocationRequest {
  oneof location {
    uint32 stock = 1;
    string cart = 2;
    uint32 delivery = 3;
    uint32 discard = 4;
  }
}

message BySkuAndLocationRequest {
  oneof location {
    uint32 stock = 1;
    string cart = 2;
    uint32 delivery = 3;
    uint32 discard = 4;
  }
  uint32 sku = 5;
}

message SetDivisibleAmountRequest {
  string upl = 1;
  uint32 divisible_amount = 2;
  uint32 created_by = 3;
}

message SetBestBeforeRequest {
  string upl = 1;
  // RFC339 or empty string
  string best_before = 2;
  uint32 created_by = 3;
}

message SplitRequest {
  string upl = 1;
  string new_upl = 2;
  uint32 piece = 3;
  uint32 created_by = 4;
}

message DivideRequest {
  string upl = 1;
  string new_upl = 2;
  uint32 requested_amount = 3;
  uint32 created_by = 4;
}

message DepreciationRequest {
  string upl = 1;
  uint32 depreciation_id = 2;
  string depreciation_comment = 3;
  uint32 created_by = 4;
}

// This should be allowed only from the inventory modul
message DepreciationRemoveRequest {
  string upl = 1;
  uint32 created_by = 2;
}

message DepreciationPriceRequest {
  string upl = 1;
  uint32 depreciation_net_price = 2;
  uint32 created_by = 3;
}

message RemoveDeprecationPriceRequest {
  string upl = 1;
  uint32 created_by = 2;
}

message InventoryLockRequest {
  string upl = 1;
  uint32 inventory_id = 2;
  uint32 created_by = 3;
}

message InventoryUnlockRequest {
  string upl = 1;
  uint32 inventory_id = 2;
  uint32 created_by = 3;
}

message CartLockRequest {
  string upl = 1;
  string cart_id = 2;
  uint32 created_by = 3;
}

message CartUnlockRequest {
  string upl = 1;
  string cart_id = 2;
  uint32 created_by = 3;
}

message CloseCartRequest {
  string cart_id = 1;
  uint32 created_by = 2;
}

message CloseInventoryRequest {
  uint32 inventory_id = 1;
  uint32 created_by = 2;
}

message UplObj {
  message KindSku { uint32 sku = 1; }
  message KindBulkSku {
    uint32 sku = 1;
    uint32 upl_pieces = 2;
  }
  message KindOpenedSku {
    uint32 sku = 1;
    uint32 amount = 2;
    repeated string successors = 3;
  }
  message KindDerivedProduct {
    string derived_from = 1;
    uint32 amount = 2;
  }
  message Depreciation {
    uint32 depreciation_id = 1;
    string depreciation_comment = 2;
  }
  // UPL ID
  string id = 1;
  // Related product id
  uint32 product_id = 2;
  uint32 sku_id = 3;
  string product_unit = 4;
  // UPL kind
  oneof kind {
    KindSku sku = 5;
    KindBulkSku bulk_sku = 6;
    KindOpenedSku opened_sku = 7;
    KindDerivedProduct derived_product = 8;
  }
  // Represents how many UPL this unit contains
  // Can bigger then 1 if UplKind is Bulk
  // Otherwise always 1
  uint32 upl_piece = 9;
  uint32 sku_divisible_amount = 10;
  // If has deprecation net price
  bool has_special_price = 11;
  // If UPL is healty: no depreciation, no best_before issue
  bool is_healty = 12;
  // Best before date (RFC3339) if there is any
  string best_before = 13;
  // Depreciation object
  Depreciation depreciation = 14;
  // Procurement id
  uint32 procurement_id = 15;
  // Procurement net price
  uint32 procurement_net_price = 16;
  uint32 procurement_net_price_sku = 17;
  // Is it divisible? Only if SKU or Derived Product and is divisible
  bool is_divisible = 18;
  // SKU VAT
  string vat = 19;
  // Related SKU net price
  uint32 price_net = 20;
  // Related SKU gross price
  uint32 price_gross = 21;
  // UPL net margin
  int32 margin_net = 22;
  // Applied lock
  oneof lock {
    string CartLock = 23;
    uint32 DeliveryLock = 24;
    uint32 InventoryLock = 25;
    google.protobuf.Empty None = 26;
  }
  // Current location
  oneof location {
    uint32 Stock = 27;
    uint32 Delivery = 28;
    string Cart = 29;
    uint32 Discard = 30;
  }
  // If upl is archived ? true : false
  bool is_archived = 31;
  // Created by user
  uint32 created_by = 32;
  // Created at RFC3339
  string created_at = 33;
}