<?php
/**
* 生産計画管理用コントローラー
*
*/
namespace App\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use App\Controller\BaseController;
use App\Service\KintoneService;
use App\Service\CommonService;
use DateTime;
class PlanControoler extends BaseController
{
protected $kintoneService;
protected $commonService;
/** @var Psr\Log\LoggerInterface logger */
protected $logger;
public function __construct(
KintoneService $kintoneService,
CommonService $commonService,
\Psr\Log\LoggerInterface $logger
)
{
parent::__construct();
$this->kintoneService = $kintoneService;
$this->commonService = $commonService;
$this->logger = $logger;
}
/**
* @param Request $request
*
* @Route("/plan", name="plan")
*/
public function index(Request $request): Response
{
return $this->getPlanData($request, "html");
}
/**
* @param Request $request
* @param string $response_type (html/json)
*/
private function getPlanData($request, $response_type) {
$data["date"] = $request->get('search_date', date("Y-m-d"));
$data["machine"] = $request->get('search_machine', "1号機");
$search["machine"] = $data["machine"];
$increment_qty = []; // 仕掛りで作る予定の数
$decrement_qty = []; // 受注して在庫から減る予定の数
$arrangement_item = []; //手配商品
$this->getRemainingData($increment_qty, $arrangement_item, $data);
$data["arrangement"] = $arrangement_item;
$start = $data["date"];
$end = date("Y-m-d", strtotime($start . '+11 day'));
//指定した出荷予定日以降+5日の受注データ取得
$result = $this->kintoneService->requestGets('order', sprintf('出荷予定日_0 >= "%s" and 出荷予定日_0 < "%s" limit 500', $data["date"], $end));
$records = $result["records"];
// 出荷する商品数をセット
foreach($records as $record) {
$sub_table = $record["受注商品テーブル"]["value"];
$delivery_date = $record["出荷予定日_0"]["value"];
//分類で生産管理の判別をする
//項目 "通常の受注", "①外部倉庫・無償サンプル発送", "②仕入れ販売・外部倉庫出庫", "③当社へ引き取り", "④発送のみ", "⑤無償サンプル引き取り"
$bunrui = $record['分類']["value"];
$target_bunrui = ['通常の受注', '①外部倉庫・無償サンプル発送', '③当社へ引き取り', '⑤無償サンプル引き取り'];
if(in_array($bunrui, $target_bunrui)) {
foreach($sub_table as $row){
$item = $row["value"];
$item_code = $item["商品コード"]["value"];
$qty = $item["箱数"]["value"] ? $item["箱数"]["value"] : 0;
if(isset($decrement_qty[$item_code])) $decrement_qty[$item_code]+= $qty;
else $decrement_qty[$item_code] = $qty;
}
}
}
//在庫マスタからデータ取得
$offset = 0;
$limit = 100;
$data["item"] = [];
$data["unarrangement"] = [];
while(1) {
$query = sprintf(' 手作業 not in ("レ") order by レコード番号 limit %d offset %d', $limit, $offset);
$result = $this->kintoneService->requestGets('stock', $query);
foreach($result["records"] as $item) {
$line = $item["製造号機"]["value"];
$film = $item["フィルムの種類"]["value"];
$film_addr = $item["フイルム住所・棚番"]["value"];
$item_name = $item["商品名"]["value"];
$item_code = $item["商品コード"]["value"];
$stock_limit = $item["最低在庫数"]["value"];
$stock = $item["在庫数"]["value"];
//商品マスタ
$data["item"][$item_code] = [
"code" => $item_code,
"name" => $item_name,
"film" => $film,
"film_addr" => $film_addr,
"line" => $line,
"limit" => $stock_limit,
"stock" => $stock
];
//生産可能リストを作成
//$qty = is_numeric($stock) ? $stock : 0;
$qty = $stock;
// +5日分の受注数を減算する
if( isset($decrement_qty[$item_code]) && is_numeric($decrement_qty[$item_code]) ) $qty-= $decrement_qty[$item_code];
// 仕掛りで作られるであろう数を加算する 仕掛り分は減らさない
if( isset($increment_qty[$item_code]) && is_numeric($increment_qty[$item_code]) ) $qty+= $increment_qty[$item_code];
// 「仕掛りがある」もしくは「在庫数が目安より少なくなった」場合は 生産可能リストとして配列に持たす
if($stock_limit > $qty || isset($increment_qty[$item_code]) ){
//未手配の商品を製造機毎に配列で持たす
foreach($line as $tmp_line){
if(!is_numeric($qty) || !is_numeric($stock_limit)) {
echo sprintf("商品コード:[%s] 在庫もしくは最低在庫数が数値ではありません。<br>kintone在庫アプリを確認してください。<br>※kintoneの修正後、反映に時間がかかる場合があります。", $item_code);
exit;
}else{
$data["unarrangement"][$tmp_line][$item_code] = [
"num" => $stock_limit > $qty ? $stock_limit - $qty : 0,
];
}
}
}
}
$offset += $limit;
if($result["totalCount"] < $offset) {
break;
}
if($offset >= 10000 ) {
echo '無限ループの可能性があります';
break;
}
}
if($response_type == "html") {
return $this->render('plan.html.twig', [
"data" => $data,
"json_items" => json_encode($data["item"]),
"json_unarrangement" => json_encode($data["unarrangement"]),
"json_arrangement" => json_encode($data["arrangement"]),
'machine_list' => $this->getOriginalConfig("param", "machines"),
"search" => $search,
]);
}else{
$params = [
"json_items" => $data["item"],
"json_unarrangement" => $data["unarrangement"],
"json_arrangement" => $data["arrangement"],
];
return new JsonResponse($params);
}
}
/**
* データの追加・修正
* @Route("/plan/set_plan", name="set_plan")
*/
public function setPlan(Request $request){
$search_date = $request->get('search_date');
$line = $request->get('line');
$set_item = $request->get('set_item');
$mode = $request->get('mode');
if($mode == "regist") $disp_mode = "追加";
elseif($mode == "edit") $disp_mode = "修正";
elseif($mode == "sort") $disp_mode = "並び替え";
$this->logger->log("debug", sprintf("update Plan date:%s line:%s items:%s:", $search_date, $line, json_encode($set_item) ));
//現状を取得
$query = sprintf('手配日 = "%s" and 製造号機 in ("%s")', $search_date, $line);
$result = $this->kintoneService->requestGets('production_management', $query);
$no = $this->commonService->convertNo($set_item["no"]);
$update_date = $this->commonService->getDateTimeUTC();
$tmp = [
"No" => $no,
"急ぎ" => $set_item["quickly"] == "1" ? ["急ぎ"] : [],
"ケース" => $set_item["num"],
"商品コード" => $set_item["item_code"],
"key" => $set_item["key"],
"最終更新日時" => $update_date
];
$fh = fopen(__DIR__ . "/log.txt", "w");
fputs($fh, print_r($tmp, true));
fclose($fh);
$new_items = [];
if(!empty($result['records'])) {
$records = $result['records'];
foreach($records as $record){
$kintone_id = $record['$id']['value'];
$table = $record['手配状況']['value'];
$exist_flg = false;
foreach($table as $item) {
if( $item['value']['key']['value'] == $set_item["key"] ) {
$exist_flg = true;
$complete_num = $item['value']['完了数']['value'];
if($tmp["ケース"] <= $complete_num) {
$tmp["完了"] = ["完了"];
}else{
$tmp["完了"] = [];
}
$new_items[] = [ "id" => $item["id"], "value" => $this->kintoneService->setAttribute($tmp) ];
}else{
$new_items[] = [ "id" => $item["id"] ];
}
}
if(!$exist_flg) {
$new_items[] = [ "value" => $this->kintoneService->setAttribute($tmp) ];
}
$log = $this->create_log($record['更新履歴']['value'], $disp_mode, $update_date);
$management["手配状況"] = $new_items;
$management["更新履歴"] = $log;
$management["全て完了"] = [];
$management_data = $this->kintoneService->setAttribute($management);
$this->kintoneService->requestPut("production_management", $kintone_id, $management_data);
}
}else{
$log = $this->create_log([], $disp_mode, $update_date);
$new_items[] = [ "value" => $this->kintoneService->setAttribute($tmp) ];
$management["製造号機"] = $line;
$management["手配日"] = $search_date;
$management["手配状況"] = $new_items;
$management["更新履歴"] = $log;
$management["手配状況"] = $new_items;
$management["全て完了"] = [];
$management_data = $this->kintoneService->setAttribute($management);
$this->kintoneService->requestPost("production_management", $management_data);
}
//return $this->getPlanData($request, "json");
return new JsonResponse(["result" => true]);
}
/**
* データの取り消し
* @Route("/plan/del_plan", name="del_plan")
*/
public function delPplan(Request $request){
$search_date = $request->get('search_date');
$line = $request->get('line');
$key = $request->get('key');
/*
テストデータ
$search_date = "2024-10-16";
$line = "4号機";
$key = "1929e7de919d9";
*/
$this->logger->log("debug", sprintf("delete Plan date:%s line:%s key:%s:", $search_date, $line, $key));
//現状を取得
$query = sprintf('手配日 = "%s" and 製造号機 in ("%s")', $search_date, $line);
$result = $this->kintoneService->requestGets('production_management', $query);
$update_date = $this->commonService->getDateTimeUTC();
foreach($result['records'] as $record) {
$kintone_id = $record['$id']['value'];
$table = $record['手配状況']['value'];
$new_items = [];
$all_complete = true;
foreach($table as $item) {
if( $item['value']['key']['value'] != $key) {
$new_items[] = ["id" => $item["id"]];
if(!in_array("完了", $item["value"]["完了"]["value"])) {
$all_complete = false;
}
}else{
$tmp = [];
$tmp["完了"] = $item["value"]["完了"]["value"];
$tmp["完了"][] = "取消し";
$tmp["最終更新日"] = $update_date;
$new_items[] = [ "id" => $item["id"], "value" => $this->kintoneService->setAttribute($tmp) ];
}
}
$management["手配状況"] = $new_items;
$management["更新履歴"] = $this->create_log($record['更新履歴']['value'], '取消し', $update_date);
if($all_complete) {
$management["全て完了"] = ["完了"];
}
$management_data = $this->kintoneService->setAttribute($management);
}
$result = $this->kintoneService->requestPut("production_management", $kintone_id, $management_data);
}
/**
* データの並び替え
* @Route("/plan/sort_plan", name="sort_plan")
*/
public function sortPlan(Request $request){
$search_date = $request->get('search_date');
$line = $request->get('line');
$items = $request->get('items');
//現状を取得
$query = sprintf('手配日 = "%s" and 製造号機 in ("%s")', $search_date, $line);
$result = $this->kintoneService->requestGets('production_management', $query);
$update_date = $this->commonService->getDateTimeUTC();
foreach($result['records'] as $record) {
$kintone_id = $record['$id']['value'];
$table = $record['手配状況']['value'];
$new_items = [];
foreach($items as $item){
foreach($table as $row) {
if($row["value"]["key"]["value"] == $item["key"]) {
$row["value"]["最終更新日"]["value"] = $update_date;
$new_items[] = $row;
break;
}
}
}
$management["手配状況"] = $new_items;
$management["更新履歴"] = $this->create_log($record['更新履歴']['value'], '並び替え', $update_date);
$management_data = $this->kintoneService->setAttribute($management);
$result = $this->kintoneService->requestPut("production_management", $kintone_id, $management_data);
}
}
/**
* 生産計画
* @Route("/manufacturing", name="manufacturing")
*/
public function manufacturing(Request $request){
$today = $request->get('today', date('Y-m-d'));
$line = $request->get('line');
//$query = sprintf('手配日 = "%s" and 製造号機 in ("%s")', $today, $line);
$query = sprintf('製造号機 in ("%s") and (手配日 = "%s" or (手配日 < "%s" and 全て完了 not in ("完了"))) order by 手配日 asc', $line, $today, $today);
$result = $this->kintoneService->requestGets('production_management', $query);
$item_codes = [];
foreach($result['records'] as $record) {
$table = $record['手配状況']['value'];
$arrangement_date = $record['手配日']['value'];
foreach($table as $row) {
if(count($row["value"]["完了"]["value"])) continue;
if(empty($row["value"]["商品コード"]["value"])) continue;
$item_code = $row["value"]["商品コード"]["value"];
if(!isset($item_codes[$arrangement_date])) $item_codes[$arrangement_date] = [];
$item_codes[$arrangement_date][] = $item_code;
$qty = (int)$row["value"]["ケース"]["value"] - (int)$row["value"]["完了数"]["value"];
if(!isset($order_qty[$arrangement_date])) $order_qty[$arrangement_date] = [];
/*
if(isset($order_qty[$arrangement_date][$item_code])) {
$order_qty[$arrangement_date][$item_code]+= $qty;
$order_no[$arrangement_date][$item_code][] = $this->commonService->convertDispNo($row["value"]["No"]["value"], $row["value"]["完了No"]["value"]);
}else{
$order_qty[$arrangement_date][$item_code] = $qty;
$order_no[$arrangement_date][$item_code][] = $this->commonService->convertDispNo($row["value"]["No"]["value"], $row["value"]["完了No"]["value"]);
}
*/
$order[$arrangement_date][] = [
"item_code" => $item_code,
"no" => $this->commonService->convertDispNo($row["value"]["No"]["value"], $row["value"]["完了No"]["value"]),
"qty" => $qty
];
}
}
$data["item"] = [];
//在庫マスタからデータ取得
foreach($item_codes as $date_val => $date_list) {
$query = sprintf('商品コード in (%s) order by レコード番号', '"' . join('","', $date_list) . '"');
$result = $this->kintoneService->requestGets('stock', $query);
foreach($result["records"] as $record) {
$film = $record["フィルムの種類"]["value"];
$film_addr = $record["フイルム住所・棚番"]["value"];
$item_name = $record["商品名"]["value"];
$item_code = $record["商品コード"]["value"];
$item[$item_code] = [
"name" => $item_name,
"film" => $film,
"film_addr" => $film_addr,
];
}
}
//リストを作成
/*
foreach($item_codes as $date_val => $date_list) {
foreach($date_list as $item_code) {
$diff = $order_qty[$date_val][$item_code];
$no = $order_no[$date_val][$item_code];
$data["item"][$date_val][] = [
"code" => $item_code,
"name" => $item[$item_code]["name"],
"no" => $no,
"film" => $item[$item_code]["film"],
"film_addr" => $item[$item_code]["film_addr"],
"diff" => $diff,
];
}
}
*/
foreach($order as $date_val => $order_val){
foreach($order_val as $val){
$item_code = $val["item_code"];
$data["item"][$date_val][] = [
"code" => $item_code,
"name" => $item[$item_code]["name"],
"no" => $val["no"],
"film" => $item[$item_code]["film"],
"film_addr" => $item[$item_code]["film_addr"],
"diff" => $val["qty"],
];
}
}
$data["line"] = $line;
return $this->render('manufacturing.html.twig', [
"today" => $today,
"data" => $data,
]);
}
/**
* 更新履歴用のデータ作成
*
* @param [type] $logs
* @param [type] $mode
* @return void
*/
private function create_log($logs, $mode, $update_date){
$new_log = [];
foreach($logs as $item){
$new_log[] = ["id" => $item["id"]];
}
$new_log[] = [ "value" => $this->kintoneService->setAttribute([
"ページ" => "生産計画管理",
"処理内容" => $mode,
"処理日時" => $update_date,
])];
return $new_log;
}
/**
* 仕掛りのデータ取得
* @param array $item_codes
*
* @return array
*/
private function getRemainingData(&$add_qty, &$arrangement_item, $data){
//完了していない手配済みリストを取得
$query = sprintf('手配日 = "%s" or (手配日 < "%s" and 完了 not in ("完了","取消し")) order by 手配日 asc', $data["date"], $data["date"]);
$result = $this->kintoneService->requestGets('production_management', $query);
$records = $result["records"];
foreach($records as $record){
$line = $record["製造号機"]["value"];
$arrangement_list = $record["手配状況"]["value"];
$complete = $record["全て完了"]["value"];
$arrangement_date = $record["手配日"]["value"];
foreach($arrangement_list as $tmp_item) {
$item = $tmp_item["value"];
$item_code = $item["商品コード"]["value"];
if(empty($item_code)) continue;
if(in_array("取消し", $item["完了"]["value"])) continue;
$num = (int)$item["ケース"]["value"];
if(!in_array("完了", $item["完了"]["value"])) {
// 設定したNoと完了したNoの差分を出す
$no = $this->commonService->convertDispNo($item["No"]["value"], $item["完了No"]["value"]);
$default_no = $this->commonService->convertDispNo($item["No"]["value"], ""); //とりあえず設定した内容を表示
$complete_num = (int)$item["完了数"]["value"];
$key = $item["key"]["value"];
$quickly = !empty($item["急ぎ"]["value"]) ? true : false;
$diff = $num - $complete_num;
if(empty($complete) && $item_code) {
$arrangement_item[$line][$arrangement_date][] = [
"item_code" => $item_code,
"num" => $num,
"no" => $no,
"default_no" => $default_no,
"complete_num" => $complete_num,
"diff" => $diff,
"key" => $key,
"quickly" => $quickly,
];
}
// 仕掛りの残り生産数を計算する
if(isset($total_arrangement[$item_code])) {
$add_qty[$item_code]+= $diff;
}else{
$add_qty[$item_code] = $diff;
}
}
}
}
}
}