---
# System prepended metadata

title: Inbound page
tags: [fulfilnet]

---

# Inbound page 

###### tags: `fulfilnet`

## Inbound Flow
> 1. client input inbound on dashboard with 
    > a. storage box
    > b. sku + its qty
    > c. tracking number + courier name 
> 2. Fulfilnet admin check qty received 
> 3. Fulfilnet admin decide storage box then print a QR code and paste on storage box
> 4. Fulfilnet admin decide location
> 5. Fulfilnet admin confirm then add qty to product


## Main affects on current system

1. can NOT update product qty anymore via other places (except SuperAdmin)
2. product uniq check changed from SKU + Location to SKU + storage box id
3. send msg 

## Terms
1. inbound: contain many inbound parcels
2. inbound parcels: contain many inbound items
3. inbount items: either new product or exsite product which plan to be ship to WH
4. storage box: store in WH location, contain many products

### inbound parcels VS storage box 

inbound parcels ~= storage box 
storage box is used when stock in warehouse! 
so if inbound parcels's set_to_storage_box = true then we will create storage box for it!


### inbound item VS product
inbound item ~= product
when inbound's product_id is empty then it mean merchant want to create this product, so once Admin confirm then we will create the product!


## User behaviors
### Merchant
1. Merchant able to click on navigation bar to inbound page
2. Merchant able to see all inbound's status
    a. `created` => just draft the inbound
    b. `sent_to_warehouse` => when has tracking number and courier name (blank field)
    c. `received` => when receive by warehouse
    d. `disputed` => when some of products qty match 
    e. `confirmed_by_warehouse` => when warehouse received and system updated QTY
4. Merchant able to see an inbound full info
5. Merchant able to create an inbound 
    a. input parcel dims and total_weight(optional)
    b. input item(sku, description, barcode(optional)) / or select product(if existed)
    c. repeat B if more than 1 item put in a parcel
    d. repeat A ~ C if more than 1 parcels
    e. input courier name
    f. put tracking number
    g. select warehouse to ship
    h. input remark (optional)
    h. click save
6. Merchant able to upload inbound via CSV
7. Merchant can delete inbound if inbound status is `created`
8. Merchant can click submit then it change status to `sent_to_warehouse`

### Warehouse Admin 
1. Admin able to click on navigation bar to inbound page
2. Admin able to see all inbounds
3. Admin able to search tracking number/status then find inbound
4. Admin able to see an inbound info
5. Admin able to update inbound 
    b. admin able to update inbound item's confirm_qty (if qty != confirm_qty, move inbound items status to `disputed`)
    c. admin able to update inbound item's remark (type demage or ...?)
    d. admin able to update inbound item's storage box (assign the inbound item to another storage box, or set inbound parcel to a storage box)
    e. admin able to click confirm inbound then if any inbound items is `disputed` then send mail to merchant, otherwise change to `confirmed_by_warehouse`. Then save qty to item.
6. Admin able to not update item qty manually (except Super admin)
7. Admin able to print QR code and paste on storage box
8. Admin able to see products under which storage boxes and its location 
9. Admin able to create storage box via CSV
10. Admin able to update storage box
11. Admin able to see all storage boxes
12. Admin able to update product's storage box
13. Admin able to see an storage box info (which products in this box)


## Database

* Merchants has many Inbounds
* Inbounds has many InboundParcels 
* InboundParcels has many InboundItems
* Warehouse has many Inbounds
* Warehouse has many StorageBoxes
* Location has many StorageBoxes
* StorageBoxes has many Products

ps. InboundItem is for product's qty


### Inbounds

```ruby=
# == Schema Information
#
# Table name: inbounds
#
#  id                                     :uuid             not null, primary key
#  warehouse_id                           :uuid
#  merchant_id                            :uuid
#  courier_name                           :string
#  tracking_number                        :string
#  remark                                 :string
#  inbound_boxes_count                    :integer
#  inbound_status                         :string 
#  created_at                             :datetime
#  updated_at                             :datetime
```

### Inbound Parcels

```ruby=
# == Schema Information
#
# Table name: inbound_parcels
#
#  id                                     :uuid             not null, primary key
#  inbound_id                             :uuid
#  length                                 :decimal
#  width                                  :decimal
#  height                                 :decimal
#  total_actual_weight                    :decimal
#  set_to_storage_box                     :boolean, default: false # admin only
#  created_at                             :datetime
#  updated_at                             :datetime
```

### Inbound Items

```ruby=
# == Schema Information
#
# Table name: inbound_items
#
#  id                                     :uuid             not null, primary key
#  inbound_parcel_id                      :uuid
#  product_id                             :uuid
#  sku                                    :string
#  quantity                               :integer
#  confirmed_quantity                     :integer
#  admin_remark                           :string
#  description                            :text
#  barcode                                :string
#  inbound_item_status                    :string
#  length                                 :decimal
#  width                                  :decimal
#  height                                 :decimal
#  actual_weight                          :decimal
#  selling_price                          :decimal(, )
#  selling_price_currency                 :integer(enum)
#  storage_box_id                         :uuid
#  created_at                             :datetime
#  updated_at                             :datetime
```

### Storage Boxes

```ruby=
# == Schema Information
#
# Table name: storage_boxes
#
#  id                                     :uuid             not null, primary key
#  warehouse_id                           :uuid
#  location_id                            :uuid
#  length                                 :decimal
#  width                                  :decimal
#  height                                 :decimal
#  total_actual_weight                    :decimal
#  created_at                             :datetime
#  updated_at                             :datetime
```

### Products

```ruby=
# == Schema Information
#
# Table name: products
#
#  ....
# storage_box_id                         :uuid

```

## Logics

1. when upload inbound CSV/create inbound items if product_id exited then mean it's for existed product otherwise it mean create products !
2. storage box QR code is from it's ID
3. when inbound is confirm by Admin, we will update/create products then assign qty and storage_box_id. then send msg to merchant if need
4. prodcut uniq check change to SKU + storage box id (before is SKU + location)
5. when inbound parcel set set_to_storage_box = true then FE will ask for location id then send to BE to create storage box!
6. once we update storage box location then system will update products location


## Tasks
[Inbound-1-BE] tables migration
[Inbound-2-BE] models: add state machine and relations and validation
[Inbound-3-0-BE] Dashboard: build normal endpoints
[Inbound-3-1-BE] Dashboard: build search/confirm endpoint
[Inbound-3-2-BE] Dashboard: build an endpoint to upload inbound via CSV
[Inbound-3-3-FE] Dashboard: navigation bar + route
[Inbound-3-4-FE] Dashboard: index page
[Inbound-3-5-FE] Dashboard: search bar
[Inbound-3-6-FE] Dashboard: create modal
[Inbound-3-7-FE] Dashboard: show modal
[Inbound-3-8-FE] Dashboard: CSV upload modal
[Inbound-3-9-FE] Dashboard: del + update status
[Inbound-4-0-BE] Admin: build storage box normal + search endpoints
[Inbound-4-1-BE] Admin: build an endpoint to upload storage box via CSV
[Inbound-4-2-BE] Admin: build an endpoint to download QR codes for storage box
[Inbound-4-3-BE] Admin: show storage box on product modal and update location to get from storage box
[Inbound-4-4-FE] Admin: storage box navigation bar + route
[Inbound-4-5-FE] Admin: storage box index page
[Inbound-4-6-FE] Admin: storage box search bar
[Inbound-4-7-FE] Admin: create / update / show modal
[Inbound-4-8-FE] Admin: storage box CSV upload modal
[Inbound-4-9-FE] Admin: storage box actions (delete/update)
[Inbound-4-10-BE] when update storage box's location, then need to update all products location
[Inbound-5-0-BE] Admin: build inbound normal endpoints + search
[Inbound-5-1-BE] Admin: build inbound confirm endpoint
[Inbound-5-2-BE] Admin: build inbound confirm service
[Inbound-5-3-BE] Admin: build inbound parcel confirm service (create storage box)
[Inbound-5-4-FE] Admin: inbound navigation bar + route
[Inbound-5-5-FE] Admin: inbound index page
[Inbound-5-6-FE] Admin: inbound search bar
[Inbound-5-6-FE] Admin: inbound update modal
[Inbound-6-BE] check and update all product uniq codes to use SKU+storage_box_id
[Inbound-7-0-BE] remove product qty and location update
[Inbound-7-1-BE] only allow Super Admin "Update" product via CSV
[Inbound-7-2-FE] Admin: remove all qty/location update from product pages
[Inbound-7-2-FE] Admin: remove all qty update from product pages