ã¯ããã«
å庫管çã·ã¹ãã ïŒWMS
: Warehouse Management SystemïŒã¯ãåšåº«ãã¹ãåºè·é
å»¶ãšãã£ãå庫ã®ã厩å£ããé²ãããã®å¿
é ããŒã«ã§ããEã³ããŒã¹ã®æ¥æé·ãç©æµããŒãºã®å€æ§åã«äŒŽããWMS
ã®éèŠæ§ã¯ãŸããŸãé«ãŸã£ãŠããŸãããã®ã·ãªãŒãºã§ã¯ãWMS
ããŒãããæ§ç¯ããå®è·µçãªçµéšãå
±æããå庫ãæããã¯ããã¯ã玹ä»ããŸãã第1åã§ã¯ãWMS
ã®åœ¹å²ãå庫æ¥åã®æ·±ãçè§£ããããŠéçºãå§ããããã®å
·äœçãªã¹ãããã詳ãã解説ããŸãã
WMSãšã¯ïŒãã®åœ¹å²
WMS
ã¯ãå庫å
ã®åšåº«ç®¡çãå
¥åºåº«æ¥åãå¹çåããã·ã¹ãã ã§ããäž»ãªæ©èœã¯ä»¥äžã®éãã§ãïŒ
- å ¥åº«ïŒååã®åãå ¥ããå質ãã§ãã¯ããã±ãŒã·ã§ã³ãžã®æ ŒçŽã
- åšåº«ç®¡çïŒãªã¢ã«ã¿ã€ã ã§ã®åšåº«è¿œè·¡ãããã管çãæ£åžã
- åºåº«ïŒæ³šæã«åºã¥ããããã³ã°ãªã¹ãäœæãæ€åãæ¢±å ã
- ã¬ããŒãïŒåšåº«ç¶æ³ãæ¥åå¹çã誀åºè·çãªã©ã®åæã
WMS
ã¯ãEã³ããŒã¹ãå°å£²ãè£œé æ¥ãªã©å€å²ã«ãããæ¥çã§æŽ»çšãããŸããããšãã°ãEã³ããŒã¹ã§ã¯ã泚æããæ°æé以å
ã«åºè·ããããŒãºã«å¯Ÿå¿ãããããWMS
ã®ãªã¢ã«ã¿ã€ã åŠçãäžå¯æ¬ ã§ããé©åãªWMS
ããªããã°ãåšåº«ãã¹ã誀åºè·ãé »çºãã顧客æºè¶³åºŠãåçã«æ·±å»ãªåœ±é¿ãäžããŸãã
å庫æ¥åã®çè§£ïŒéçºã®ç¬¬äžæ©
WMS
éçºãæåãããã«ã¯ãå庫æ¥åã®ãããŒãæ·±ãçè§£ããããšãå¿
é ã§ãã以äžã¯ãå
žåçãªå庫æ¥åã®æµããšãããããã®èª²é¡ã§ãïŒ
-
å
¥åº«ïŒReceivingïŒïŒ
- ãã©ãã¯ããååãéãããããŒã³ãŒããã¹ãã£ã³ããŠã·ã¹ãã ã«ç»é²ã
- å質ãã§ãã¯ãè¡ããæå®ããããã±ãŒã·ã§ã³ïŒæ£ããŸãŒã³ïŒã«æ ŒçŽã
- 課é¡ïŒããŒã³ãŒãã®èªã¿åããšã©ãŒãååæ å ±ã®äžäžèŽãæ ŒçŽãã¹ã
- äŸïŒããå庫ã§ã¯ãããŒã³ãŒãã¹ãã£ããŒã®é å»¶ã«ãããå ¥åº«ã«1ååããã10ç§äœèšã«ãããã1æ¥1000ååã§çŽ3æéã®ãã¹ãçºçã
-
åšåº«ç®¡çïŒInventory ManagementïŒïŒ
- ååã®ç§»åãæ£åžããªã¢ã«ã¿ã€ã ã§èšé²ã
- ãããçªå·ãæå¹æéã®è¿œè·¡ïŒäŸïŒé£åãå»è¬åïŒã
- 課é¡ïŒåšåº«ãã¹ãæ£åžã®æéããããã
- äŸïŒåšåº«ããŒã¿ãæŽæ°ããããå®éã«ã¯åšåº«ããªãååã販売ããã顧客ã¯ã¬ãŒã ã«çºå±ã
-
åºåº«ïŒShippingïŒïŒ
- 泚æã«åºã¥ããŠãããã³ã°ãªã¹ããäœæã
- ååããããã³ã°ããããŒã³ãŒãã§æ€ååŸã梱å ããŠåºè·ã
- 課é¡ïŒèª€åºè·ããããã³ã°ã®éå¹çãªã«ãŒãã
- äŸïŒãããã³ã°ãªã¹ããçŽããŒã¹ã§ãååã®ãã±ãŒã·ã§ã³ãäžæç¢ºãªããããããã³ã°ã«äœèšãªæéããããã
-
æ£åžïŒCycle CountingïŒïŒ
- 宿çã«åšåº«ããã§ãã¯ããã·ã¹ãã ãšå®éã®åšåº«ãäžèŽãããã
- 課é¡ïŒé »ç¹ãªæ£åžã«ããæ¥å忢ãåšåº«å·®ç°ã®çºèŠé å»¶ã
- äŸïŒææ¬¡æ£åžã§æ¥åã忥忢ããç¹å¿æã®åºè·ãé å»¶ã
å®è·µäŸïŒå庫ã¹ã¿ãããšã®å¯Ÿè©±
éçºãå§ããåã«ãå庫ã¹ã¿ããã«ã€ã³ã¿ãã¥ãŒããããšãåŒ·ãæšå¥šããŸããçè ã®çµéšã§ã¯ãããEã³ããŒã¹å庫ã§ä»¥äžã®ãããªèª²é¡ãæµ®ã圫ãã«ãªããŸããïŒ
- ããããã³ã°ãªã¹ããçŽããŒã¹ã§ãååã®ãã±ãŒã·ã§ã³ãåããã«ãããæ£çªå·ãæžããŠããã°å©ãããã
- ãããŒã³ãŒãã¹ãã£ããŒã®åå¿ãé ããå ¥åº«ã«æéãããããç¹ã«å€ãããã€ã¹ã§é¡èãã
- ãåšåº«ç®¡çããŒã¿ããªã¢ã«ã¿ã€ã ã§æŽæ°ãããªãå Žåãããã誀åºè·ãçºçãããã
ãããã®ãã£ãŒãããã¯ãåºã«ã以äžã®ãããªèšèšæ¹éãç«ãŠãŸããïŒ
-
ãããã³ã°ãªã¹ãã«ãã±ãŒã·ã§ã³ã³ãŒãïŒäŸïŒ
A-01-01
ïŒãæèšã - é«éãªããŒã³ãŒãã¹ãã£ã³æ©èœãã¢ãã€ã«ã¢ããªã«å®è£ ã
-
åšåº«ç®¡çããªã¢ã«ã¿ã€ã ã§è¡ã
WebSocket
ããŒã¹ã®éç¥ã·ã¹ãã ãæ€èšã
ãã®ãããªå¯Ÿè©±ãéããŠãã·ã¹ãã ã®ãŠãŒã¶ããªãã£ãå€§å¹ ã«åäžãããããšãã§ããŸãã
æè¡ã¹ã¿ãã¯ã®éžæ
WMS
ã¯é«ãä¿¡é Œæ§ãã¹ã±ãŒã©ããªãã£ããŠãŒã¶ããªãã£ãæ±ãããããããé©åãªæè¡ã¹ã¿ãã¯ãéžã¶ããšãéèŠã§ãã以äžã¯ãæšå¥šæè¡ã¹ã¿ãã¯ã®äžäŸãšãã®çç±ã§ãïŒ
- ããã¯ãšã³ãïŒ
Python
(Flask
ãŸãã¯Django
)- éçºé床ãéããå庫æ¥åã®è€éãªããžãã¯ïŒäŸïŒãããã³ã°ã«ãŒãæé©åïŒãæ±ããããã
- è±å¯ãªã©ã€ãã©ãªïŒäŸïŒ
SQLAlchemy
ãPsycopg2
ïŒã§ããŒã¿ããŒã¹æäœãç°¡åã
- ããŒã¿ããŒã¹ïŒ
PostgreSQL
- è€éãªåšåº«ç®¡çã¯ãšãªïŒäŸïŒè€æ°ãã±ãŒã·ã§ã³ã®åšåº«éèšïŒã«æé©ã
- ãã©ã³ã¶ã¯ã·ã§ã³ç®¡çãå€éšããŒå¶çŽã§ããŒã¿ã®æŽåæ§ã確ä¿ã
- ããã³ããšã³ãïŒ
React
- ã¢ãã€ã«ãã¬ã³ããªãŒãªUIãè¿ éã«æ§ç¯å¯èœã
- å庫ã¹ã¿ããã䜿ããããçŽæçãªã€ã³ã¿ãŒãã§ãŒã¹ïŒäŸïŒå€§ããªãã¿ã³ãã·ã³ãã«ãªãã©ãŒã ïŒã
-
APIïŒ
REST API
- å€éšã·ã¹ãã ïŒäŸïŒEã³ããŒã¹ãã©ãããã©ãŒã ãERPïŒãšã®çµ±åã dá» dà ngã«ããã
- ã¢ãã€ã«ã¢ããªãããŒã³ãŒãã¹ãã£ããŒãšã®é£æºããµããŒãã
æè¡éžå®ã®ãã€ã³ã
-
ã¹ã±ãŒã©ããªãã£ïŒåæã¯å°æ°ã®å庫ã察象ãšããŠããå°æ¥ã¯è€æ°ååº«ãæ°çŸäžSKUã«å¯Ÿå¿ã§ãããããæ¡åŒµæ§ãèæ
®ãããšãã°ã
PostgreSQL
ã®ããŒãã£ã·ã§ãã³ã°ãRedis
ãã£ãã·ã¥ãèšç»ã - ãŠãŒã¶ããªãã£ïŒå庫ã¹ã¿ããã¯ITã«äžæ £ããªå Žåãå€ããããã·ã³ãã«ã§çŽæçãªUI/UXãåªå ãäŸïŒã¢ãã€ã«ç»é¢ã§ã¿ãããããã倧ããªãã¿ã³ã
- ã³ã¹ãïŒãªãŒãã³ãœãŒã¹æè¡ã掻çšããŠåææè³ãæãããã¯ã©ãŠãïŒ
AWS
ãGCP
ïŒã§ã®ãããã€ãèŠéã«å ¥ããã
MVPããå§ãã
ãã¹ãŠã®æ©èœãäžåºŠã«éçºããã®ã¯éçŸå®çã§ãããŸãã¯MVPïŒMinimum Viable Product
ïŒãšããŠãåºæ¬çãªå
¥åº«æ©èœãæ§ç¯ããŸããããMVPã®ç®æšã¯ãå庫ã¹ã¿ãããååãã·ã¹ãã ã«ç»é²ããåšåº«ç®¡çã§ããæå°éã®æ©èœãæäŸããããšã§ãã
以äžã¯ãFlask
ã䜿ã£ãååç»é²ã®APIãšã³ããã€ã³ãã®äŸã§ãããã®APIã¯ãåååãSKUãåšåº«æ°ãç»é²ããåºæ¬çãªããªããŒã·ã§ã³ãè¡ããŸãã
from flask import Flask, request, jsonify
from http import HTTPStatus
app = Flask(__name__)
# ä»®ã®ããŒã¿ããŒã¹ïŒå®éã¯PostgreSQLã䜿çšïŒ
products = []
@app.route('/api/products', methods=['POST'])
def add_product():
data = request.get_json()
# ããªããŒã·ã§ã³
required_fields = ['name', 'sku', 'quantity']
if not all(field in data for field in required_fields):
return jsonify({'error': 'å¿
èŠãªãã£ãŒã«ããäžè¶³ããŠããŸã'}), HTTPStatus.BAD_REQUEST
if data['quantity'] < 0:
return jsonify({'error': 'æ°éã¯0以äžã§ãªããã°ãªããŸãã'}), HTTPStatus.BAD_REQUEST
# SKUã®éè€ãã§ãã¯
if any(p['sku'] == data['sku'] for p in products):
return jsonify({'error': 'SKUããã§ã«ååšããŸã'}), HTTPStatus.CONFLICT
# ååç»é²
product = {
'id': len(products) + 1,
'name': data['name'],
'sku': data['sku'],
'quantity': data['quantity']
}
products.append(product)
return jsonify({
'message': 'ååãç»é²ããŸãã',
'product': product
}), HTTPStatus.CREATED
if __name__ == '__main__':
app.run(debug=True)
APIã®äœ¿çšäŸ
以äžã®cURL
ã³ãã³ãã§ååãç»é²ã§ããŸãïŒ
curl -X POST http://localhost:5000/api/products \
-H "Content-Type: application/json" \
-d '{"name": "Tã·ã£ã", "sku": "TSHIRT001", "quantity": 100}'
ã¬ã¹ãã³ã¹äŸïŒ
{
"message": "ååãç»é²ããŸãã",
"product": {
"id": 1,
"name": "Tã·ã£ã",
"sku": "TSHIRT001",
"quantity": 100
}
}
ãšã©ãŒã±ãŒã¹ã®äŸ
SKUãéè€ããå ŽåïŒ
curl -X POST http://localhost:5000/api/products \
-H "Content-Type: application/json" \
-d '{"name": "Tã·ã£ã2", "sku": "TSHIRT001", "quantity": 50}'
ã¬ã¹ãã³ã¹ïŒ
{
"error": "SKUããã§ã«ååšããŸã"
}
MVPã®å®è£ ãã€ã³ã
- ã·ã³ãã«ãïŒå ¥åº«æ©èœã«çŠç¹ãåœãŠãåºåº«ãã¬ããŒãã¯åŸã§è¿œå ãMVPã§ã¯ãååç»é²ãšåºæ¬çãªåšåº«ç®¡çãåªå ã
- ããªããŒã·ã§ã³ïŒå ¥åããŒã¿ã®æ£ç¢ºæ§ãä¿èšŒïŒäŸïŒæ°éãè² ã§ãªããSKUã®éè€ãã§ãã¯ïŒã
- ãšã©ãŒãã³ããªã³ã°ïŒãããããããšã©ãŒã¡ãã»ãŒãžãè¿ãããŠãŒã¶ãŒã®æ··ä¹±ãé²ããäŸïŒã
SKUããã§ã«ååšããŸã
ãã¯ãå庫ã¹ã¿ãããããçè§£ã§ããã - ã¢ãã€ã«å¯Ÿå¿ïŒAPIã¯ã¢ãã€ã«ã¢ããªãããŒã³ãŒãã¹ãã£ããŒãšé£æºããããšãæ³å®ãã軜éã§é«éã«èšèšã
å®è·µã®ãã€ã³ã
- ãŠãŒã¶ãŒäžå¿ã®èšèšïŒå庫ã¹ã¿ããã䜿ãããããŠãŒã¶ããªãã£ãæèãããšãã°ãã¢ãã€ã«ç»é¢ã§ã¯ã¿ãããããã倧ããªãã¿ã³ãã·ã³ãã«ãªãã©ãŒã ãæ¡çšãçè ã®ãããžã§ã¯ãã§ã¯ãã¹ã¿ããããå ¥åé ç®ãå€ãããããšäžæºãæŒããããããå¿ é ãã£ãŒã«ããæå°éã«çµããŸããã
- ãã¹ãã®åŸ¹åºïŒåšåº«ç®¡çããŒã¿ã¯æ£ç¢ºæ§ãåœãå°ããªãã°ã誀åºè·ãåšåº«ãã¹ãåŒãèµ·ãããããåäœãã¹ããæåããå®è£ ãäŸïŒSKUéè€ãã§ãã¯ã®ãã¹ãã±ãŒã¹ã远å ã
- ããã¥ã¡ã³ãïŒAPIã®ä»æ§ïŒãšã³ããã€ã³ããå ¥å/åºå圢åŒïŒãå庫æ¥åãããŒãææžåããå°æ¥ã®ã¡ã³ããã³ã¹ãããŒã ã³ã©ãã¬ãŒã·ã§ã³ã容æã«ããã
ãã¹ãäŸ
以äžã¯ãäžèšAPIã®ç°¡åãªåäœãã¹ãã§ãïŒPython
ã®unittest
ã䜿çšïŒïŒ
import unittest
from app import app
class TestProductAPI(unittest.TestCase):
def setUp(self):
self.app = app.test_client()
self.app.testing = True
def test_add_product_success(self):
response = self.app.post('/api/products', json={
'name': 'Tã·ã£ã',
'sku': 'TSHIRT001',
'quantity': 100
})
self.assertEqual(response.status_code, 201)
self.assertIn('ååãç»é²ããŸãã', response.get_json()['message'])
def test_add_product_missing_field(self):
response = self.app.post('/api/products', json={
'name': 'Tã·ã£ã',
'quantity': 100
})
self.assertEqual(response.status_code, 400)
self.assertIn('å¿
èŠãªãã£ãŒã«ããäžè¶³ããŠããŸã', response.get_json()['error'])
def test_add_product_duplicate_sku(self):
self.app.post('/api/products', json={
'name': 'Tã·ã£ã',
'sku': 'TSHIRT001',
'quantity': 100
})
response = self.app.post('/api/products', json={
'name': 'Tã·ã£ã2',
'sku': 'TSHIRT001',
'quantity': 50
})
self.assertEqual(response.status_code, 409)
self.assertIn('SKUããã§ã«ååšããŸã', response.get_json()['error'])
if __name__ == '__main__':
unittest.main()
ãã®ãã¹ãã¯ãæ£åžžãªååç»é²ãå¿ é ãã£ãŒã«ãã®æ¬ åŠãSKUéè€ã®ãšã©ãŒã±ãŒã¹ãæ€èšŒããŸãã
åŠäŒã®ãã€ã³ã
å庫ã¹ã¿ãããšã®å¯Ÿè©±ãæåã®éµïŒéçºãå§ããåã«ãå庫ã¹ã¿ããã«ã€ã³ã¿ãã¥ãŒããå®éã®å庫æ¥åã課é¡ãææ¡ããŸããããçè ã®çµéšã§ã¯ããããããžã§ã¯ãã§ã¹ã¿ããã®ãã£ãŒãããã¯ãç¡èŠããçµæããããã³ã°ãªã¹ãã®UIã䜿ãã¥ãããæ¥åå¹çãäœäžããŸãããããšãã°ãããããã³ã°ãªã¹ãã«ãã±ãŒã·ã§ã³ãæèšãããŠããªãããããŒã³ãŒãã¹ãã£ã³ãé ãããšãã£ã声ãåæ ããããšã§ãã·ã¹ãã ã®ãŠãŒã¶ããªãã£ãå€§å¹ ã«åäžããŸãããæåã®å¯Ÿè©±ã«æéããããããšã¯ãé·æçãªéçºã³ã¹ããåæžããŸãã
次åäºå
次åã¯ãWMS
ã®ããŒã¿ããŒã¹èšèšã«ã€ããŠè§£èª¬ããŸããååãåšåº«ããã±ãŒã·ã§ã³ãªã©ã®ãšã³ãã£ãã£ãã©ã®ããã«ã¢ãã«åããã¹ã±ãŒã©ãã«ã§æè»ãªã¹ããŒããæ§ç¯ããããå
·äœçãªSQL
äŸã亀ããŠç޹ä»ããŸãã