diff --git a/apps/batch-sim/index.js b/apps/batch-sim/index.js
index 3aa975010502986f4fe776f30d98ec711900765d..714f3a283f815c8dd1fb57c5bf1f67ec07819bec 100644
--- a/apps/batch-sim/index.js
+++ b/apps/batch-sim/index.js
@@ -1,5 +1,6 @@
 // COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022.
 
+const faker = require("@faker-js/faker")
 const { Option } = require("commander")
 
 const { createCLIApp, HypercogApiClient } = require("@hypercog/utils")
@@ -38,22 +39,22 @@ const simulateCycle = async (arg1, cmd) => {
   await signInAs(apiClient, otherArgs.sidenorUser, password)
 
   let batchId = await createAssetAndSendToCone(apiClient, "simulated asset 1")
-  batchId = await createAssetAndSendToCone(apiClient, "simulated asset 2")
-  batchId = await createAssetAndSendToCone(apiClient, "simulated asset 3")
+  //batchId = await createAssetAndSendToCone(apiClient, "simulated asset 2")
+  //batchId = await createAssetAndSendToCone(apiClient, "simulated asset 3")
 
   const selectedStock = await sendToStock(apiClient, { assetId: batchId })
 
   await signInAs(apiClient, otherArgs.cement1User, password)
   await bidAsset(apiClient, {
     assetId: selectedStock.id,
-    price: 2
+    price: faker.datatype.number({ min: 50, max: 150 })
   })
 
   await signInAs(apiClient, otherArgs.cement2User, password)
   await bidAsset(apiClient, {
     assetId: selectedStock.id,
     quantity: selectedStock.quantity - 500,
-    price: 3
+    price: faker.datatype.number({ min: 25, max: 200 })
   })
 
   await signInAs(apiClient, otherArgs.sidenorUser, password)
diff --git a/apps/batch-sim/package-lock.json b/apps/batch-sim/package-lock.json
index 860683f2e1dfb02d38d8dfd7131fde7b7eef18e8..1c2e6800244c1d9ba8aa1492dba18caa3a1ec211 100644
--- a/apps/batch-sim/package-lock.json
+++ b/apps/batch-sim/package-lock.json
@@ -6,6 +6,7 @@
 		"": {
 			"name": "@hypercog/batch-sim",
 			"dependencies": {
+				"@faker-js/faker": "^5.5.3",
 				"commander": "^9.0.0"
 			}
 		},
@@ -38,6 +39,11 @@
 				"inquirer": "^8.2.0"
 			}
 		},
+		"node_modules/@faker-js/faker": {
+			"version": "5.5.3",
+			"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz",
+			"integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw=="
+		},
 		"node_modules/commander": {
 			"version": "9.0.0",
 			"resolved": "https://registry.npmjs.org/commander/-/commander-9.0.0.tgz",
@@ -48,6 +54,11 @@
 		}
 	},
 	"dependencies": {
+		"@faker-js/faker": {
+			"version": "5.5.3",
+			"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz",
+			"integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw=="
+		},
 		"commander": {
 			"version": "9.0.0",
 			"resolved": "https://registry.npmjs.org/commander/-/commander-9.0.0.tgz",
diff --git a/apps/batch-sim/package.json b/apps/batch-sim/package.json
index d547ac0d7c26fab53bff897ce6473e839fa12bc2..98dd94580d9805d3fabfedf93318a035d2392ee8 100644
--- a/apps/batch-sim/package.json
+++ b/apps/batch-sim/package.json
@@ -4,6 +4,7 @@
     "@hypercog/utils": "^0.0.1",
     "@hypercog/sidenor-cli": "^0.0.1",
     "@hypercog/cement-cli": "^0.0.1",
+    "@faker-js/faker": "^5.5.3",
     "commander": "^9.0.0"
   }
 }
diff --git a/apps/cement-cli/lib.js b/apps/cement-cli/lib.js
index 6f6dfce8a711d519374a274570ac8c1824564bb3..fdd44dc4c76d19d0a5417aefe043526ff88f116c 100644
--- a/apps/cement-cli/lib.js
+++ b/apps/cement-cli/lib.js
@@ -19,7 +19,8 @@ const bidAsset = async (
       {
         type: "number",
         name: "inputQuantity",
-        message: `Choose an ammount of slag (max: ${assetToBid.quantity} ${assetToBid.units}):`
+        message: `Choose an ammount of slag (max: ${assetToBid.quantity} ${assetToBid.units}):`,
+        default: Math.floor(assetToBid.quantity * 0.1) // Default: 10%
       }
     ])
     chosenQuantity = inputQuantity
diff --git a/apps/pubadmin-cli/index.js b/apps/pubadmin-cli/index.js
index 922c5a33a7b633b9df53ea77f5db5d9d50c4c414..86ddc4bea9011ed2b2b6162e693e8d069164736e 100644
--- a/apps/pubadmin-cli/index.js
+++ b/apps/pubadmin-cli/index.js
@@ -39,35 +39,45 @@ const humanizePercentage = value => {
   if (value.percentage === undefined) {
     return value.total
   }
-  return `${value.total.toFixed(3)} [%${(value.percentage * 100).toFixed(2)}]`
+  return `${value.total.toFixed(3)} [${(value.percentage * 100).toFixed(2)} %]`
 }
 const humanizePrice = value =>
   `${value.avg.toFixed(2)} [${value.min.toFixed(2)}, ${value.max.toFixed(2)}]`
 
-const toLine = stat => [
+const toSteelLine = stat => [
   stat.total.toFixed(3),
   humanizePercentage(stat.reused),
   humanizePercentage(stat.discarded),
   humanizePrice(stat.price)
 ]
 
+const toCementLine = stat => [stat.total.toFixed(3), humanizePrice(stat.price)]
+
 const getStats = async apiClient => {
   const stats = await apiClient.getStats()
 
-  const tableValues = [
+  let tableValues = [
     [
       "",
       "Total slag\nt",
       "Reused\nt [%]",
       "Discarded\nt [%]",
-      "Price (€)\n Avg [Min, Max]"
+      "Price (€/t)\n Avg [Min, Max]"
     ],
     ...Object.entries(stats.perSteelCompany || []).map(([org, val]) => [
       org,
-      ...toLine(val)
+      ...toSteelLine(val)
     ])
   ]
+  console.log(table(tableValues, TABLE_CONFIG))
 
+  tableValues = [
+    ["", "Total slag\nt", "Price (€/t)\n Avg [Min, Max]"],
+    ...Object.entries(stats.perCementCompany || []).map(([org, val]) => [
+      org,
+      ...toCementLine(val)
+    ])
+  ]
   console.log(table(tableValues, TABLE_CONFIG))
 }
 
diff --git a/apps/sidenor-cli/lib.js b/apps/sidenor-cli/lib.js
index 5039deb25f60ea9f3f1044afa76d9316966859b5..08404177fc643daea019bc0a25c0ac1e2699247c 100644
--- a/apps/sidenor-cli/lib.js
+++ b/apps/sidenor-cli/lib.js
@@ -199,7 +199,7 @@ const bidResponse =
           name: "inputBidder",
           message: `Select the bidder whose bid will be ${action}ed`,
           choices: assetSelected.fields.bids.map(b => ({
-            name: `${b.bidder.id} (quantity: ${b.quantity} ${assetSelected.units}, price: ${b.price})`,
+            name: `${b.bidder.id} (quantity: ${b.quantity} ${assetSelected.units}, price: ${b.price} €)`,
             value: b.bidder
           }))
         }
diff --git a/chaincode/controller/bid/bid_controller.go b/chaincode/controller/bid/bid_controller.go
index 16503d78fbdbd7ecf5754d8c312b1810ba6d1919..77056a52dc2e0cabf066a6b8ae78b165ab1f4a73 100644
--- a/chaincode/controller/bid/bid_controller.go
+++ b/chaincode/controller/bid/bid_controller.go
@@ -1,7 +1,8 @@
-// controller.go
-// COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2018.
-// controller.go is part of the Computer file TRACEBLOCK. Copyrighted under U.S. Copyright Office Registration Number TXu002105160, registered on 2018-06-25 by FUNDACIÓN TECNALIA RESEARCH & INNOVATION.
-// This license is effective worldwide.
+/**
+ * bid_input.go
+ *
+ * COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022.
+ */
 
 package bid
 
@@ -15,6 +16,8 @@ import (
 	"github.com/mitchellh/mapstructure"
 
 	"git.code.tecnalia.com/blockchain/hypercog/controller/stats"
+	hmodel "git.code.tecnalia.com/blockchain/hypercog/model"
+
 	errs "git.code.tecnalia.com/traceblock/sdk/constants"
 	"git.code.tecnalia.com/traceblock/sdk/controller/base"
 	"git.code.tecnalia.com/traceblock/sdk/controller/split"
@@ -30,28 +33,6 @@ type BidDecisionController struct {
 	BidController
 }
 
-type BidStatus string
-
-const (
-	Proposed = "proposed"
-	Accepted = "accepted"
-	Rejected = "rejected"
-)
-
-type Bidder struct {
-	Id	 	string `json:"id"`
-	Role	string `json:"role"`
-	Org		string `json:"org"`
-}
-
-type Bid struct {
-	// Not renaming any of these fields in json to make mapstructure.decode work
-	Quantity    uint32		`json:"quantity"`
-	Price   	float32		`json:"price"`
-	Bidder	 	Bidder		`json:"bidder"`
-	Status	 	BidStatus	`json:"status"`
-}
-
 var (
 	errInvalidInputData     = errors.New("failed to read model")
 	errParamsMissing        = errors.New("missing split params. assetId and splitParams config must be send in split request")
@@ -101,8 +82,8 @@ func (c BidController) readAsset(stub shared.LedgerBuildrStubInterface, requestA
 	return c.readTraceableAsset(stub, requestAsset.GetID())
 }
 
-func (c BidController) getBids(biddableAsset *model.TraceableAsset) ([]Bid, error) {
-	bidArr := []Bid{}
+func (c BidController) getBids(biddableAsset *model.TraceableAsset) ([]hmodel.Bid, error) {
+	bidArr := []hmodel.Bid{}
 	if _, ok := biddableAsset.ArbitraryDataFields["bids"]; ok {
 		// to avoid interface conversion errors ([]interface {} is not []bid.Bid)
 		err := mapstructure.Decode(biddableAsset.ArbitraryDataFields["bids"], &bidArr)
@@ -147,15 +128,15 @@ func (c BidController) BidAsset(stub shared.LedgerBuildrStubInterface, params sh
 		return api.NewApiResponsePtr(fnName, err, nil).SendResponse()
 	}
 
-	newBid := Bid{
+	newBid := hmodel.Bid{
 		Quantity: bidParams.Bid.Quantity,
 		Price: bidParams.Bid.Price,
-		Bidder: Bidder{
+		Bidder: hmodel.Bidder{
 			Id: stub.GetUniqueUserId(),
 			Org: org,
 			Role: role,
 		},
-		Status: Proposed,
+		Status: hmodel.Proposed,
 	}
 
 	alreadyExists := false
@@ -177,7 +158,7 @@ func (c BidController) BidAsset(stub shared.LedgerBuildrStubInterface, params sh
 	return c.SaveAbstractAsset(stub, biddableAsset)
 }
 
-func (c BidDecisionController) bidResponse(stub shared.LedgerBuildrStubInterface, params shared.LedgerBuildrAsset, acceptBid bool)  (*model.TraceableAsset, *Bid, error) {
+func (c BidDecisionController) bidResponse(stub shared.LedgerBuildrStubInterface, params shared.LedgerBuildrAsset, acceptBid bool)  (*model.TraceableAsset, *hmodel.Bid, error) {
 	bidDecisionParams, ok := params.(*BidDecisionParams)
 	if bidDecisionParams == nil || !ok {
 		//failed when casting/fetching input data
@@ -194,14 +175,14 @@ func (c BidDecisionController) bidResponse(stub shared.LedgerBuildrStubInterface
 		return nil, nil, err
 	}
 
-	var selectedBid *Bid = nil
+	var selectedBid *hmodel.Bid = nil
 	for i, bid := range auxArr {
         if bid.Bidder.Id == bidDecisionParams.Bidder  {
 			selectedBid = &bid
 			if acceptBid {
-				auxArr[i].Status = Accepted
+				auxArr[i].Status = hmodel.Accepted
 			} else {
-				auxArr[i].Status = Rejected
+				auxArr[i].Status = hmodel.Rejected
 			}
 			break
         }
@@ -284,8 +265,8 @@ func  (c BidDecisionController) _acceptBid(stub shared.LedgerBuildrStubInterface
 	transferParams.Location = soldStock.Location
 
 	transferredAsset := transfer.TransferAsset(stub, soldStock, transferParams)
-	
-	err = stats.RegisterSale(stub, soldStock.Quantity, bid.Price, soldStock.Units)
+
+	err = stats.RegisterSale(stub, bid, soldStock.Units)
 	if err != nil {
 		return nil, err
 	}
diff --git a/chaincode/controller/stats/stats_controller.go b/chaincode/controller/stats/stats_controller.go
index f8bf0c1eae82648fd03552d72a9a3f448705675a..4a00f7b175d1b868ba6b7ec8bf8f3dec2ba29b0b 100644
--- a/chaincode/controller/stats/stats_controller.go
+++ b/chaincode/controller/stats/stats_controller.go
@@ -7,7 +7,7 @@
 package stats
 
 import (
-	"git.code.tecnalia.com/blockchain/hypercog/model"
+	"git.code.tecnalia.com/blockchain/hypercog/model/stats"
 	"git.code.tecnalia.com/ledgerbuilder/sdk/core/api"
 	"git.code.tecnalia.com/ledgerbuilder/sdk/core/fabric/protos"
 	"git.code.tecnalia.com/ledgerbuilder/sdk/shared"
@@ -15,7 +15,8 @@ import (
 
 
 type StatsResult struct {
-	PerSteelCompany map[string]model.Stats `json:"perSteelCompany,omitempty"`
+	PerSteelCompany map[string]stats.SteelStats `json:"perSteelCompany,omitempty"`
+	PerCementCompany map[string]stats.CementStats `json:"perCementCompany,omitempty"`
 }
 
 func GetStatsOperation(stub shared.LedgerBuildrStubInterface, request shared.LedgerBuildrAsset) protos.Response {
@@ -23,23 +24,41 @@ func GetStatsOperation(stub shared.LedgerBuildrStubInterface, request shared.Led
 
 	ret := new(StatsResult)
 
-	orgs, err := getOrgList(stub)
+	orgs, err := getSteelOrgList(stub)
 	if err != nil {
 		return api.NewApiResponsePtr(fnName, err, nil).SendResponse()
 	}
 
 	if len(*orgs) > 0 {
-		ret.PerSteelCompany = make(map[string]model.Stats)
+		ret.PerSteelCompany = make(map[string]stats.SteelStats)
 	}
 
 	for _, org := range *orgs {
-		orgStat, err := getStats(stub, org)
+		orgStat, err := getSteelStats(stub, org)
 		if err != nil {
 			return api.NewApiResponsePtr(fnName, err, nil).SendResponse()
 		}
 		ret.PerSteelCompany[org] = *orgStat.RefineStats()
     }
 
+
+	orgs, err = getCementOrgList(stub)
+	if err != nil {
+		return api.NewApiResponsePtr(fnName, err, nil).SendResponse()
+	}
+
+	if len(*orgs) > 0 {
+		ret.PerCementCompany = make(map[string]stats.CementStats)
+	}
+
+	for _, org := range *orgs {
+		orgStat, err := getCementStats(stub, org)
+		if err != nil {
+			return api.NewApiResponsePtr(fnName, err, nil).SendResponse()
+		}
+		ret.PerCementCompany[org] = *orgStat.RefineStats()
+    }
+
 	return api.NewAPIGenericResponsePtr(fnName, nil, ret).SendResponse()
 }
 
diff --git a/chaincode/controller/stats/stub.go b/chaincode/controller/stats/stub.go
index 590c281d972d35125926758476c390ef2127bfbe..26359f5f61993d04129e6b97e6160bb1c660dfd6 100644
--- a/chaincode/controller/stats/stub.go
+++ b/chaincode/controller/stats/stub.go
@@ -11,21 +11,41 @@ import (
 	"fmt"
 	"math"
 
-	"git.code.tecnalia.com/blockchain/hypercog/model"
 	"git.code.tecnalia.com/ledgerbuilder/sdk/core/util/logging"
 	"git.code.tecnalia.com/ledgerbuilder/sdk/shared"
+
+	"git.code.tecnalia.com/blockchain/hypercog/model"
+	"git.code.tecnalia.com/blockchain/hypercog/model/stats"
 )
 
 const STEEL_ORG_LIST_KEY = "steel_orgs"
+const CEMENT_ORG_LIST_KEY = "cement_orgs"
 
 type OrgList []string
 
 var log = logging.NewGoLogger("stats")
 
-func getStats(stub shared.LedgerBuildrStubInterface, key string) (st *model.RawStats, err error) {
+func getSteelStats(stub shared.LedgerBuildrStubInterface, key string) (st *stats.RawSteelOrgStats, err error) {
+	value, err := stub.GetState(key)
+
+	if err != nil || len(value) == 0 {
+		st = new(stats.RawSteelOrgStats)
+		st.MinPrice = math.MaxFloat64
+	} else {
+		err = json.Unmarshal(value, &st)
+		if err != nil  {
+			return nil, err
+		}
+	}
+
+	return st, nil
+}
+
+func getCementStats(stub shared.LedgerBuildrStubInterface, key string) (st *stats.RawCementOrgStats, err error) {
 	value, err := stub.GetState(key)
+
 	if err != nil || len(value) == 0 {
-		st = new(model.RawStats)
+		st = new(stats.RawCementOrgStats)
 		st.MinPrice = math.MaxFloat64
 	} else {
 		err = json.Unmarshal(value, &st)
@@ -37,7 +57,7 @@ func getStats(stub shared.LedgerBuildrStubInterface, key string) (st *model.RawS
 	return st, nil
 }
 
-func putStats(stub shared.LedgerBuildrStubInterface, key string, stats *model.RawStats) (error) {
+func putJson(stub shared.LedgerBuildrStubInterface, key string, stats interface{}) (error) {
 	serialized, err := json.Marshal(stats)
 	if err != nil {
 		return err
@@ -51,8 +71,8 @@ func putStats(stub shared.LedgerBuildrStubInterface, key string, stats *model.Ra
 	return nil
 }
 
-func getOrgList(stub shared.LedgerBuildrStubInterface) (st *OrgList, err error) {
-	value, err := stub.GetState(STEEL_ORG_LIST_KEY)
+func _getOrgList(stub shared.LedgerBuildrStubInterface, key string) (st *OrgList, err error) {
+	value, err := stub.GetState(key)
 	if err != nil || len(value) == 0 {
 		st = new(OrgList)
 	} else {
@@ -65,8 +85,26 @@ func getOrgList(stub shared.LedgerBuildrStubInterface) (st *OrgList, err error)
 	return st, nil
 }
 
+func getSteelOrgList(stub shared.LedgerBuildrStubInterface) (st *OrgList, err error) {
+	return _getOrgList(stub, STEEL_ORG_LIST_KEY)
+}
+
+func getCementOrgList(stub shared.LedgerBuildrStubInterface) (st *OrgList, err error) {
+	return _getOrgList(stub, CEMENT_ORG_LIST_KEY)
+}
+
 func resetStats(stub shared.LedgerBuildrStubInterface) (error) {
-	orgs, err := getOrgList(stub)
+	orgs, err := getSteelOrgList(stub)
+	if len(*orgs) > 0 {
+		for _, org := range *orgs {
+			err = stub.DelState(org)
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	orgs, err = getCementOrgList(stub)
 	if len(*orgs) > 0 {
 		for _, org := range *orgs {
 			err = stub.DelState(org)
@@ -77,6 +115,8 @@ func resetStats(stub shared.LedgerBuildrStubInterface) (error) {
 	}
 
 	err = stub.DelState(STEEL_ORG_LIST_KEY)
+	err = stub.DelState(CEMENT_ORG_LIST_KEY)
+
 	return err
 }
 
@@ -89,8 +129,8 @@ func (orgs OrgList) contains(org string) (bool) {
 	return false
 }
 
-func addOrgIfNotExist(stub shared.LedgerBuildrStubInterface, org string) (err error) {
-	orgs, err := getOrgList(stub)
+func addOrgIfNotExist(stub shared.LedgerBuildrStubInterface, key string, org string) (err error) {
+	orgs, err := _getOrgList(stub, key)
 	if err != nil {
 		return err
 	}
@@ -103,7 +143,7 @@ func addOrgIfNotExist(stub shared.LedgerBuildrStubInterface, org string) (err er
 			return err
 		}
 	
-		err = stub.PutState(STEEL_ORG_LIST_KEY, serialized)
+		err = stub.PutState(key, serialized)
 		if err != nil {
 			return err
 		}
@@ -115,13 +155,13 @@ func addOrgIfNotExist(stub shared.LedgerBuildrStubInterface, org string) (err er
 }
 
 
-func retrieveExistingStats(stub shared.LedgerBuildrStubInterface) (*model.RawStats, error) {
+func retrieveExistingStats(stub shared.LedgerBuildrStubInterface) (*stats.RawSteelOrgStats, error) {
 	org, err := stub.GetOrganization()
 	if err != nil {
 		return nil, err
 	}
 
-	customStats, err := getStats(stub, org)
+	customStats, err := getSteelStats(stub, org)
 	if err != nil {
 		return nil, err
 	}
@@ -129,18 +169,18 @@ func retrieveExistingStats(stub shared.LedgerBuildrStubInterface) (*model.RawSta
 	return customStats, nil
 }
 
-func upgradeStats(stub shared.LedgerBuildrStubInterface, customStats *model.RawStats) (error) {
+func upgradeSteelStats(stub shared.LedgerBuildrStubInterface, customStats *stats.RawSteelOrgStats) (error) {
 	org, err := stub.GetOrganization()
 	if err != nil {
 		return err
 	}
 
-	err = putStats(stub, org, customStats)
+	err = putJson(stub, org, customStats)
 	if err != nil {
 		return err
 	}
 
-	err = addOrgIfNotExist(stub, org)
+	err = addOrgIfNotExist(stub, STEEL_ORG_LIST_KEY, org)
 	if err != nil {
 		return err
 	}
@@ -149,6 +189,20 @@ func upgradeStats(stub shared.LedgerBuildrStubInterface, customStats *model.RawS
 	return nil
 }
 
+func upgradeCementStats(stub shared.LedgerBuildrStubInterface, customStats *stats.RawCementOrgStats, org string) (error) {
+	err := putJson(stub, org, customStats)
+	if err != nil {
+		return err
+	}
+
+	err = addOrgIfNotExist(stub, CEMENT_ORG_LIST_KEY, org)
+	if err != nil {
+		return err
+	}
+
+	log.Debug("Upgrading stats")
+	return nil
+}
 
 func RegisterSlag(stub shared.LedgerBuildrStubInterface, quantity uint32, units string) (error) {
 	orgStats, err := retrieveExistingStats(stub)
@@ -158,7 +212,7 @@ func RegisterSlag(stub shared.LedgerBuildrStubInterface, quantity uint32, units
 
 	orgStats.RegisterSlag(quantity, units)
 
-	err = upgradeStats(stub, orgStats)
+	err = upgradeSteelStats(stub, orgStats)
 	if err != nil {
 		return err
 	}
@@ -166,20 +220,33 @@ func RegisterSlag(stub shared.LedgerBuildrStubInterface, quantity uint32, units
 	return nil
 }
 
-func RegisterSale(stub shared.LedgerBuildrStubInterface, quantity uint32, price float32, units string) (error) {
-	orgStats, err := retrieveExistingStats(stub)
+func RegisterSale(stub shared.LedgerBuildrStubInterface, acceptedBid *model.Bid, units string) (error) {
+	senderOrgStats, err := retrieveExistingStats(stub)
+	if err != nil {
+		return err
+	}
+
+	senderOrgStats.UpgradeSale(acceptedBid.Quantity, acceptedBid.Price, units)
+
+	err = upgradeSteelStats(stub, senderOrgStats)
 	if err != nil {
 		return err
 	}
-	
-	orgStats.UpgradeSale(quantity, price, units)
 
-	log.Debug(fmt.Sprintf("Sale registered in stats (quantity: %d %s, price: %.2f €)", quantity, units, price))
 
-	err = upgradeStats(stub, orgStats)
+	receiverOrgStats, err := getCementStats(stub, acceptedBid.Bidder.Org)
 	if err != nil {
 		return err
 	}
 
+	receiverOrgStats.UpgradeSale(acceptedBid.Quantity, acceptedBid.Price, units)
+		
+	err = upgradeCementStats(stub, receiverOrgStats, acceptedBid.Bidder.Org)
+	if err != nil {
+		return err
+	}
+
+
+	log.Debug(fmt.Sprintf("Sale registered in stats (quantity: %d %s, price: %.2f €)", acceptedBid.Quantity, units, acceptedBid.Price))
 	return nil
 }
\ No newline at end of file
diff --git a/chaincode/model/bid.go b/chaincode/model/bid.go
new file mode 100644
index 0000000000000000000000000000000000000000..c24537aa897835f3e57033477d40c61e38fb805e
--- /dev/null
+++ b/chaincode/model/bid.go
@@ -0,0 +1,29 @@
+/**
+ * bid.go
+ *
+ * COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022.
+ */
+
+package model
+
+type BidStatus string
+
+const (
+	Proposed = "proposed"
+	Accepted = "accepted"
+	Rejected = "rejected"
+)
+
+type Bidder struct {
+	Id	 	string `json:"id"`
+	Role	string `json:"role"`
+	Org		string `json:"org"`
+}
+
+type Bid struct {
+	// Not renaming any of these fields in json to make mapstructure.decode work
+	Quantity    uint32		`json:"quantity"`
+	Price   	float32		`json:"price"`
+	Bidder	 	Bidder		`json:"bidder"`
+	Status	 	BidStatus	`json:"status"`
+}
diff --git a/chaincode/model/stats.go b/chaincode/model/stats.go
deleted file mode 100644
index b63e8e60be9020001188ceb78f3d60445928df59..0000000000000000000000000000000000000000
--- a/chaincode/model/stats.go
+++ /dev/null
@@ -1,89 +0,0 @@
-/**
- * stats.go
- *
- * COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022.
- */
-
-package model
-
-type PercentageMetric struct {
-	Total float64 `json:"total"` // in tons
-	Percentage float64 `json:"percentage,omitempty"` // in %
-}
-
-type PriceMetric struct {
-	Minimum float64 `json:"min"`
-	Maximum float64 `json:"max"`
-	Average float64 `json:"avg"`
-}
-
-// Total or per company
-// Refined stats which will be returned
-type Stats struct {
-	TotalSlag float64 `json:"total"` // in tons
-	Reused PercentageMetric `json:"reused"`
-	Discarded PercentageMetric `json:"discarded"`
-	Price PriceMetric `json:"price"`
-}
-
-// Total or per company
-// Raw stored stats
-type RawStats struct {
-	TotalSlag float64 `json:"total"` // in tons
-	SlagReused float64 `json:"reused"` // in tons
-	SlagDiscarded float64 `json:"discarded"` // in tons
-	TotalPrice float64 `json:"totalPrice"` // in €
-	BidAmount uint32 `json:"bidAmount"` // in units
-	MinPrice float64 `json:"minPrice"` // in €
-	MaxPrice float64 `json:"maxPrice"` // in €
-}
-
-func toTons(quantity uint32, units string) (float64) {
-	amount := float64(quantity)
-	if units == "kg" {
-		amount /= 1000.0
-	}
-	return amount
-}
-
-func (raw *RawStats) RegisterSlag(quantity uint32, units string) {
-	raw.TotalSlag += toTons(quantity, units)
-}
-
-func (raw *RawStats) UpgradeSale(quantity uint32, price float32, units string) {
-	raw.SlagReused += toTons(quantity, units)
-	raw.TotalPrice += float64(price)
-	raw.BidAmount += 1
-
-	convertedPrice := float64(price)
-	if convertedPrice < raw.MinPrice {
-		raw.MinPrice = convertedPrice
-	}
-
-	if raw.MaxPrice < convertedPrice {
-		raw.MaxPrice = convertedPrice
-	}
-} 
-
-func (raw *RawStats) RefineStats() (*Stats) {
-	ret := new(Stats)
-	ret.TotalSlag = raw.TotalSlag
-
-	ret.Reused.Total = raw.SlagReused
-	if ret.TotalSlag > 0 && ret.Reused.Total > 0 {
-		ret.Reused.Percentage = ret.Reused.Total / ret.TotalSlag
-	}
-
-	ret.Discarded.Total = raw.SlagDiscarded
-	if ret.TotalSlag > 0 && ret.Discarded.Total > 0 {
-		ret.Discarded.Percentage = ret.Discarded.Total / ret.TotalSlag
-	}
-
-	ret.Price.Minimum = raw.MinPrice
-	ret.Price.Maximum = raw.MaxPrice
-	if raw.BidAmount > 0 {
-		ret.Price.Average = raw.TotalPrice / float64(raw.BidAmount)
-	}
-
-	return ret
-}
diff --git a/chaincode/model/stats/cement.go b/chaincode/model/stats/cement.go
new file mode 100644
index 0000000000000000000000000000000000000000..b2e01ba867426f27cd0aad7f23139f7203c6f451
--- /dev/null
+++ b/chaincode/model/stats/cement.go
@@ -0,0 +1,50 @@
+/**
+ * cement.go
+ *
+ * COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022.
+ */
+
+package stats
+
+// Refined stats which will be returned
+type CementStats struct {
+	TotalSlag float64 `json:"total"` // in tons
+	SoldBatch float64 `json:"sold"` // in t/sold batch
+	Price SummaryMetric `json:"price"` // in t/€
+}
+
+// Raw stored stats (per cement company)
+type RawCementOrgStats struct {
+	RawStats
+}
+
+func (raw *RawCementOrgStats) UpgradeSale(quantity uint32, price float32, units string) {
+	soldBatch := toTons(quantity, units)
+	raw.TotalSlag += soldBatch
+	raw.TotalPrice += float64(price)
+	raw.SoldBatches += 1
+
+	convertedPrice := float64(price) / soldBatch
+	if convertedPrice < raw.MinPrice {
+		raw.MinPrice = convertedPrice
+	}
+
+	if raw.MaxPrice < convertedPrice {
+		raw.MaxPrice = convertedPrice
+	}
+}
+
+func (raw *RawCementOrgStats) RefineStats() (*CementStats) {
+	ret := new(CementStats)
+	ret.TotalSlag = raw.TotalSlag
+
+	ret.SoldBatch = ret.TotalSlag / float64(raw.SoldBatches)
+
+	ret.Price.Minimum = raw.MinPrice
+	ret.Price.Maximum = raw.MaxPrice
+	if raw.TotalSlag > 0 {
+		ret.Price.Average = raw.TotalPrice / raw.TotalSlag
+	}
+
+	return ret
+}
diff --git a/chaincode/model/stats/common.go b/chaincode/model/stats/common.go
new file mode 100644
index 0000000000000000000000000000000000000000..df52ba77e168e7f8c92c6982dd5e0f20086323dd
--- /dev/null
+++ b/chaincode/model/stats/common.go
@@ -0,0 +1,35 @@
+/**
+ * common.go
+ *
+ * COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022.
+ */
+
+package stats
+
+type PercentageMetric struct {
+	Total float64 `json:"total"` // in tons
+	Percentage float64 `json:"percentage,omitempty"` // in %
+}
+
+type SummaryMetric struct {
+	Minimum float64 `json:"min"`
+	Maximum float64 `json:"max"`
+	Average float64 `json:"avg"`
+}
+
+// Generic raw stats
+type RawStats struct {
+	TotalSlag float64 `json:"total"` // in tons
+	TotalPrice float64 `json:"totalPrice"` // in €
+	SoldBatches uint32 `json:"soldBatches"` // in units
+	MinPrice float64 `json:"minPrice"` // in €
+	MaxPrice float64 `json:"maxPrice"` // in €
+}
+
+func toTons(quantity uint32, units string) (float64) {
+	amount := float64(quantity)
+	if units == "kg" {
+		amount /= 1000.0
+	}
+	return amount
+}
diff --git a/chaincode/model/stats/steel.go b/chaincode/model/stats/steel.go
new file mode 100644
index 0000000000000000000000000000000000000000..14020c08d2860ce5ad2a1a844b4959b0e54a816a
--- /dev/null
+++ b/chaincode/model/stats/steel.go
@@ -0,0 +1,70 @@
+/**
+ * steel.go
+ *
+ * COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022.
+ */
+
+package stats
+
+// Refined stats which will be returned
+type SteelStats struct {
+	TotalSlag float64 `json:"total"` // in tons
+	Reused PercentageMetric `json:"reused"`
+	Discarded PercentageMetric `json:"discarded"`
+	SoldBatch float64 `json:"sold"` // in t/sold batch
+	Price SummaryMetric `json:"price"` // in t/€
+}
+
+// Raw stored stats (per steel company)
+type RawSteelOrgStats struct {
+	RawStats
+
+	SlagReused float64 `json:"reused"` // in tons
+	SlagDiscarded float64 `json:"discarded"` // in tons
+}
+
+
+func (raw *RawSteelOrgStats) RegisterSlag(quantity uint32, units string) {
+	raw.TotalSlag += toTons(quantity, units)
+}
+
+func (raw *RawSteelOrgStats) UpgradeSale(quantity uint32, price float32, units string) {
+	soldBatch := toTons(quantity, units)
+	raw.SlagReused += soldBatch
+	raw.TotalPrice += float64(price)
+	raw.SoldBatches += 1
+
+	convertedPrice := float64(price) / soldBatch
+	if convertedPrice < raw.MinPrice {
+		raw.MinPrice = convertedPrice
+	}
+
+	if raw.MaxPrice < convertedPrice {
+		raw.MaxPrice = convertedPrice
+	}
+} 
+
+func (raw *RawSteelOrgStats) RefineStats() (*SteelStats) {
+	ret := new(SteelStats)
+	ret.TotalSlag = raw.TotalSlag
+
+	ret.Reused.Total = raw.SlagReused
+	if ret.TotalSlag > 0 && ret.Reused.Total > 0 {
+		ret.Reused.Percentage = ret.Reused.Total / ret.TotalSlag
+	}
+
+	ret.Discarded.Total = raw.SlagDiscarded
+	if ret.TotalSlag > 0 && ret.Discarded.Total > 0 {
+		ret.Discarded.Percentage = ret.Discarded.Total / ret.TotalSlag
+	}
+
+	ret.SoldBatch = ret.TotalSlag / float64(raw.SoldBatches)
+
+	ret.Price.Minimum = raw.MinPrice
+	ret.Price.Maximum = raw.MaxPrice
+	if raw.TotalSlag > 0 {
+		ret.Price.Average = raw.TotalPrice / raw.TotalSlag
+	}
+
+	return ret
+}
\ No newline at end of file