Переглянути джерело

Created demo site with sandbox database

debrand
Harry Groves 1 місяць тому
джерело
коміт
a84a750dae
27 змінених файлів з 1853 додано та 386 видалено
  1. +127
    -0
      docs/DEMO_BRANCH_SETUP.md
  2. +16
    -0
      src/main/java/com/ffii/fpsms/api/service/ApiCallerService.kt
  3. +94
    -0
      src/main/java/com/ffii/fpsms/modules/common/demo/DemoCoverageSeeder.kt
  4. +37
    -0
      src/main/java/com/ffii/fpsms/modules/common/demo/DemoExternalBlockFilter.kt
  5. +684
    -0
      src/main/java/com/ffii/fpsms/modules/common/demo/DemoSandboxSeeder.kt
  6. +18
    -18
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt
  7. +2
    -2
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderAssignmentService.kt
  8. +5
    -5
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderQueryService.kt
  9. +9
    -9
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderService.kt
  10. +52
    -52
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoReleaseCoordinatorService.kt
  11. +1
    -1
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoReplenishmentService.kt
  12. +13
    -13
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchDopoAssignmentService.kt
  13. +67
    -67
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchMainService.kt
  14. +31
    -31
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchReleaseService.kt
  15. +70
    -70
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt
  16. +1
    -1
      src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLineRepository.kt
  17. +3
    -3
      src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt
  18. +14
    -14
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/HierarchicalFgPayloadAssembler.kt
  19. +3
    -3
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt
  20. +79
    -79
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt
  21. +13
    -13
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderWorkbenchService.kt
  22. +3
    -1
      src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/PurchaseOrderController.kt
  23. +2
    -2
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLineRepository.kt
  24. +23
    -0
      src/main/resources/application-db-demo.yml
  25. +484
    -0
      src/main/resources/demo/demo-coverage.sql
  26. +1
    -1
      src/main/resources/jasper/SemiFGProductionAnalysisReport.jrxml
  27. +1
    -1
      src/main/resources/jasper/StockItemConsumptionTrendReport.jrxml

+ 127
- 0
docs/DEMO_BRANCH_SETUP.md Переглянути файл

@@ -0,0 +1,127 @@
# Demo branch setup (`debrand`)

This branch is a white-label demo. It uses a separate database, `fpsms_demo`, filled with fictional data. It must not read or write the live company database `fpsmsdb`, and it must not call the live M18 system.

Check out `debrand` in both repositories:

- `FPSMS-backend`
- `FPSMS-frontend`

Do not merge this branch. Do not start it with `db-local`, `prod`, or `db-toa`.

## What you get


| | |
| -------------- | --------------------------------------------------------------- |
| Site | Frontend on port 3000, API on port 8090 |
| Spring profile | `db-demo` |
| Database | `fpsms_demo` on `127.0.0.1:3306` |
| App login | `demo` / `Demo2026!` |
| Also | `demo.stock` / `Demo2026!` (倉務), `demo.prod` / `Demo2026!` (出品) |


`demo` has ADMIN, STOCK, PROD, PACK, and REPORT_MGMT. The UI language switch still works. Item names and other master data are stored in Chinese.

On the report page, leave the dates empty for most downloads. For 「庫存盤點報告」 choose round 「示範盤點輪次」. For 「送貨訂單與倉存單位不符報告」 use today’s date.

## 1. Stop anything already on port 8090

The frontend on this branch calls `http://localhost:8090/api`. If a normal backend is already listening there, it is the live database. Stop that process before starting the demo.

## 2. Copy the table structure into `fpsms_demo`

Liquibase changesets in this repo name the schema `fpsmsdb`. Running them against an empty database, as `root`, would change the live database. Do not do that.

Use the local `root` account from `application-db-local.yml` only for this one-time copy. Create an empty schema, load the structure (no rows), then copy the Liquibase history so startup treats the schema as already up to date.

```bash
mysql -h127.0.0.1 -P3306 -uroot -p -e \
"CREATE DATABASE IF NOT EXISTS fpsms_demo CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"

mysqldump --no-data --routines --triggers -h127.0.0.1 -P3306 -uroot -p fpsmsdb \
> /tmp/fpsms_demo_schema.sql

mysql -h127.0.0.1 -P3306 -uroot -p fpsms_demo < /tmp/fpsms_demo_schema.sql

mysqldump -h127.0.0.1 -P3306 -uroot -p fpsmsdb DATABASECHANGELOG DATABASECHANGELOGLOCK \
> /tmp/fpsms_demo_changelog.sql

mysql -h127.0.0.1 -P3306 -uroot -p fpsms_demo < /tmp/fpsms_demo_changelog.sql
```

`fpsms_demo` should now have tables and Liquibase history, and no company rows.

## 3. Create a login that cannot see `fpsmsdb`

`application-db-demo.yml` connects as `fpsms_demo` / `FpsmsDemoOnly2026`. That account is allowed to use `fpsms_demo` only.

```bash
mysql -h127.0.0.1 -P3306 -uroot -p <<'SQL'
CREATE USER IF NOT EXISTS 'fpsms_demo'@'127.0.0.1' IDENTIFIED BY 'FpsmsDemoOnly2026';
CREATE USER IF NOT EXISTS 'fpsms_demo'@'localhost' IDENTIFIED BY 'FpsmsDemoOnly2026';
ALTER USER 'fpsms_demo'@'127.0.0.1' IDENTIFIED BY 'FpsmsDemoOnly2026';
ALTER USER 'fpsms_demo'@'localhost' IDENTIFIED BY 'FpsmsDemoOnly2026';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER,
CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW,
CREATE ROUTINE, ALTER ROUTINE, EVENT, TRIGGER
ON fpsms_demo.* TO 'fpsms_demo'@'127.0.0.1';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER,
CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW,
CREATE ROUTINE, ALTER ROUTINE, EVENT, TRIGGER
ON fpsms_demo.* TO 'fpsms_demo'@'localhost';
FLUSH PRIVILEGES;
SQL
```

Confirm the wall before starting the app. The first command must fail with MySQL error 1142. The second must succeed.

```bash
mysql -h127.0.0.1 -P3306 -ufpsms_demo -pFpsmsDemoOnly2026 -e "SELECT COUNT(*) FROM fpsmsdb.user;"
mysql -h127.0.0.1 -P3306 -ufpsms_demo -pFpsmsDemoOnly2026 -D fpsms_demo -e "SELECT COUNT(*) FROM user;"
```

Do not put `root` back into `application-db-demo.yml`. If this login can read `fpsmsdb`, the demo profile refuses to start. That refusal is the point.

## 4. Start the backend

JDK 17. From `FPSMS-backend`, on branch `debrand`:

```bash
./gradlew bootRun --args='--spring.profiles.active=db-demo'
```

The first boot inserts the fictional sandbox (`DemoSandboxSeeder`) and the extra report rows (`src/main/resources/demo/demo-coverage.sql`). Later boots skip both when user `demo` and setting `DEMO.coverage.v1` already exist.

In the log, expect:

`Demo database login cannot read live schema fpsmsdb (MySQL error 1142).`

Do not expect `Token updated`. That line means the process asked the live M18 server for a token.

M18 screens are hidden. Calling them directly returns 403, for example `GET /api/m18/test/po-by-code`.

## 5. Start the frontend

From `FPSMS-frontend`, on branch `debrand`:

```bash
npm install
npm run dev
```

Open `http://localhost:3000`. `.env.development` and `.env.production` on this branch both point at port 8090 on this machine. Do not point `NEXT_PUBLIC_API_URL` at `10.10.0.81` or any other live server.

Sign in as `demo` / `Demo2026!`.

## If startup fails


| Log | What to do |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| Can read live schema `fpsmsdb` | The datasource user is too powerful. Recreate `fpsms_demo` as in step 3. Do not bypass the check. |
| Access denied for `fpsms_demo` | The user or password does not match `application-db-demo.yml`, or the grants were not flushed. |
| Liquibase tries to alter `fpsmsdb` | The changelog tables were not copied into `fpsms_demo`. Repeat step 2. With the restricted login this should fail closed, not change live data. |
| Port 8090 already in use | Stop the other backend. Do not point the demo frontend at it. |



+ 16
- 0
src/main/java/com/ffii/fpsms/api/service/ApiCallerService.kt Переглянути файл

@@ -8,6 +8,7 @@ import com.ffii.fpsms.m18.model.M18TokenResponse
import jakarta.annotation.PostConstruct
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
@@ -38,6 +39,7 @@ import java.util.concurrent.atomic.AtomicReference
@Service
open class ApiCallerService(
val m18Config: M18Config,
@Value("\${fpsms.demo-mode:false}") @PublishedApi internal val demoMode: Boolean,
) {
val logger: Logger = LoggerFactory.getLogger(ApiCallerService::class.java)
private val MAX_IN_MEMORY_SIZE = 10 * 1024 * 1024
@@ -70,10 +72,19 @@ open class ApiCallerService(

@PostConstruct
fun init() {
if (demoMode) {
logger.warn("Demo mode: M18 API is disabled. No token request will be sent.")
return
}
updateToken().subscribe()
}

@PublishedApi
internal fun <T> demoBlocked(): Mono<T> =
Mono.error(IllegalStateException("M18 is disabled in demo mode"))

fun updateToken() : Mono<String>{
if (demoMode) return demoBlocked()
val params = M18TokenRequest(
grant_type = m18Config.GRANT_TYPE,
client_id = m18Config.CLIENT_ID,
@@ -173,6 +184,7 @@ open class ApiCallerService(
params: MultiValueMap<String, String>,
customHeaders: Map<String, String>?
): Mono<T> {
if (demoMode) return demoBlocked()
println("ACCESS TOKEN: ${m18Config.ACCESS_TOKEN}")

return webClient.get()
@@ -300,6 +312,7 @@ open class ApiCallerService(
params: MultiValueMap<String, String>,
customHeaders: Map<String, String>?
): Mono<T> {
if (demoMode) return demoBlocked()
println("ACCESS TOKEN: ${m18Config.ACCESS_TOKEN}")

return webClient.post()
@@ -344,6 +357,7 @@ open class ApiCallerService(
bodyJson: String,
customHeaders: Map<String, String>? = null
): Mono<T> {
if (demoMode) return demoBlocked()
val contentType = MediaType.APPLICATION_JSON
val utf8Bytes = bodyJson.toByteArray(StandardCharsets.UTF_8)
val bodyBuffer: DataBuffer = DefaultDataBufferFactory.sharedInstance.wrap(utf8Bytes)
@@ -385,6 +399,7 @@ open class ApiCallerService(
bodyJson: String,
customHeaders: Map<String, String>? = null
): Mono<T> {
if (demoMode) return demoBlocked()
val encodedParam = URLEncoder.encode(bodyJson, StandardCharsets.UTF_8).replace("+", "%20")
val base = m18Config.BASE_URL.removeSuffix("/")
val path = urlPath.removePrefix("/")
@@ -430,6 +445,7 @@ open class ApiCallerService(
body: Any,
customHeaders: Map<String, String>? = null
): Mono<T> {
if (demoMode) return demoBlocked()
return webClient.put()
.uri { uriBuilder -> uriBuilder.path(urlPath).queryParams(queryParams).build() }
.headers { headers -> customHeaders?.forEach { (k, v) -> headers.set(k, v) } }


+ 94
- 0
src/main/java/com/ffii/fpsms/modules/common/demo/DemoCoverageSeeder.kt Переглянути файл

@@ -0,0 +1,94 @@
package com.ffii.fpsms.modules.common.demo

import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.ApplicationRunner
import org.springframework.context.annotation.Profile
import org.springframework.core.annotation.Order
import org.springframework.core.io.ClassPathResource
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.stereotype.Component
import java.nio.charset.StandardCharsets
import java.sql.Connection
import javax.sql.DataSource

/**
* Extra fictional rows so demo screens and report downloads have something to show.
* Runs only after the base sandbox exists, and only against fpsms_demo.
*/
@Component
@Profile("db-demo")
@Order(2)
class DemoCoverageSeeder(
@Value("\${spring.datasource.jdbc-url}") private val jdbcUrl: String,
private val dataSource: DataSource,
private val jdbcTemplate: JdbcTemplate,
) : ApplicationRunner {

private val log = LoggerFactory.getLogger(DemoCoverageSeeder::class.java)

override fun run(args: ApplicationArguments) {
val configuredName = databaseName(jdbcUrl)
val connectedName = dataSource.connection.use { it.catalog }
if (configuredName != DEMO_DATABASE || !DEMO_DATABASE.equals(connectedName, ignoreCase = true)) {
throw IllegalStateException(
"Demo coverage refused to start. Configured database is '$configuredName', connected database is '$connectedName'.",
)
}
val already = jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM settings WHERE name = ?",
Int::class.java,
MARKER,
) ?: 0
if (already > 0) {
log.info("Demo coverage data already present.")
return
}
val script = ClassPathResource(SCRIPT).inputStream.use { it.readBytes().toString(StandardCharsets.UTF_8) }
dataSource.connection.use { connection ->
connection.autoCommit = false
try {
executeScript(connection, script)
connection.commit()
} catch (ex: Exception) {
connection.rollback()
throw IllegalStateException("Demo coverage data failed to load.", ex)
}
}
log.info("Demo coverage data loaded.")
}

private fun executeScript(connection: Connection, script: String) {
val statement = StringBuilder()
connection.createStatement().use { jdbc ->
for (rawLine in script.lineSequence()) {
val line = rawLine.trim()
if (line.isEmpty() || line.startsWith("--")) continue
statement.append(rawLine).append('\n')
if (line.endsWith(";")) {
val sql = statement.toString().trim().removeSuffix(";").trim()
statement.clear()
if (sql.isNotEmpty()) jdbc.execute(sql)
}
}
}
val tail = statement.toString().trim().removeSuffix(";").trim()
if (tail.isNotEmpty()) {
connection.createStatement().use { it.execute(tail) }
}
}

private fun databaseName(jdbcUrl: String): String? {
val path = jdbcUrl.substringBefore('?').substringAfter("://")
val slash = path.indexOf('/')
if (slash < 0 || slash == path.lastIndex) return null
return path.substring(slash + 1).trim().ifEmpty { null }
}

companion object {
private const val DEMO_DATABASE = "fpsms_demo"
private const val MARKER = "DEMO.coverage.v1"
private const val SCRIPT = "demo/demo-coverage.sql"
}
}

+ 37
- 0
src/main/java/com/ffii/fpsms/modules/common/demo/DemoExternalBlockFilter.kt Переглянути файл

@@ -0,0 +1,37 @@
package com.ffii.fpsms.modules.common.demo

import jakarta.servlet.FilterChain
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.beans.factory.annotation.Value
import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.stereotype.Component
import org.springframework.web.filter.OncePerRequestFilter

/**
* Demo profile must not read or write the live M18 system, even if a client calls the API directly.
*/
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
class DemoExternalBlockFilter(
@Value("\${fpsms.demo-mode:false}") private val demoMode: Boolean,
) : OncePerRequestFilter() {

override fun shouldNotFilter(request: HttpServletRequest): Boolean {
if (!demoMode) return true
val path = request.requestURI
return !path.contains("/m18") && !path.contains("/scheduler/trigger")
}

override fun doFilterInternal(
request: HttpServletRequest,
response: HttpServletResponse,
filterChain: FilterChain,
) {
response.status = HttpServletResponse.SC_FORBIDDEN
response.contentType = "application/json"
response.characterEncoding = "UTF-8"
response.writer.write("""{"message":"M18 is disabled in demo mode"}""")
}
}

+ 684
- 0
src/main/java/com/ffii/fpsms/modules/common/demo/DemoSandboxSeeder.kt Переглянути файл

@@ -0,0 +1,684 @@
package com.ffii.fpsms.modules.common.demo

import com.ffii.fpsms.m18.entity.M18DataLog
import com.ffii.fpsms.m18.entity.M18DataLogRepository
import com.ffii.fpsms.m18.enums.M18DataLogStatus
import com.ffii.fpsms.modules.deliveryOrder.entity.DeliveryOrder
import com.ffii.fpsms.modules.deliveryOrder.entity.DeliveryOrderLine
import com.ffii.fpsms.modules.deliveryOrder.entity.DeliveryOrderLineRepository
import com.ffii.fpsms.modules.deliveryOrder.entity.DeliveryOrderRepository
import com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderLineStatus
import com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderStatus
import com.ffii.fpsms.modules.jobOrder.entity.JobOrder
import com.ffii.fpsms.modules.jobOrder.entity.JobOrderBomMaterial
import com.ffii.fpsms.modules.jobOrder.entity.JobOrderBomMaterialRepository
import com.ffii.fpsms.modules.jobOrder.entity.JobOrderRepository
import com.ffii.fpsms.modules.jobOrder.enums.JobOrderBomMaterialStatus
import com.ffii.fpsms.modules.jobOrder.enums.JobOrderStatus
import com.ffii.fpsms.modules.master.entity.Bom
import com.ffii.fpsms.modules.master.entity.BomMaterial
import com.ffii.fpsms.modules.master.entity.BomMaterialRepository
import com.ffii.fpsms.modules.master.entity.BomRepository
import com.ffii.fpsms.modules.master.entity.Currency
import com.ffii.fpsms.modules.master.entity.CurrencyRepository
import com.ffii.fpsms.modules.master.entity.ItemUom
import com.ffii.fpsms.modules.master.entity.ItemUomRespository
import com.ffii.fpsms.modules.master.entity.Items
import com.ffii.fpsms.modules.master.entity.ItemsRepository
import com.ffii.fpsms.modules.master.entity.Shop
import com.ffii.fpsms.modules.master.entity.ShopRepository
import com.ffii.fpsms.modules.master.entity.UomConversion
import com.ffii.fpsms.modules.master.entity.UomConversionRepository
import com.ffii.fpsms.modules.master.entity.Warehouse
import com.ffii.fpsms.modules.master.entity.WarehouseRepository
import com.ffii.fpsms.modules.master.enums.BomStatus
import com.ffii.fpsms.modules.master.enums.ShopType
import com.ffii.fpsms.modules.master.web.models.ItemType
import com.ffii.fpsms.modules.pickOrder.entity.PickOrder
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLine
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderLineStatus
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderType
import com.ffii.fpsms.modules.productProcess.entity.ProductProcess
import com.ffii.fpsms.modules.productProcess.entity.ProductProcessRepository
import com.ffii.fpsms.modules.productProcess.enums.ProductProcessStatus
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrder
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderLine
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderLineRepository
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderStatus
import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderType
import com.ffii.fpsms.modules.stock.entity.Inventory
import com.ffii.fpsms.modules.stock.entity.InventoryLot
import com.ffii.fpsms.modules.stock.entity.InventoryLotLine
import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository
import com.ffii.fpsms.modules.stock.entity.InventoryLotRepository
import com.ffii.fpsms.modules.stock.entity.InventoryRepository
import com.ffii.fpsms.modules.stock.entity.StockIn
import com.ffii.fpsms.modules.stock.entity.StockInLine
import com.ffii.fpsms.modules.stock.entity.StockInLineRepository
import com.ffii.fpsms.modules.stock.entity.StockInRepository
import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus
import com.ffii.fpsms.modules.user.entity.User
import com.ffii.fpsms.modules.user.entity.UserRepository
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.ApplicationRunner
import org.springframework.context.annotation.Profile
import org.springframework.core.annotation.Order
import org.springframework.dao.DataAccessException
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.stereotype.Component
import org.springframework.transaction.support.TransactionTemplate
import java.math.BigDecimal
import java.sql.SQLException
import java.time.LocalDate
import java.time.LocalDateTime
import javax.sql.DataSource

/**
* Fictional sandbox for the db-demo profile. Inserts nothing unless the JDBC database is fpsms_demo.
*/
@Component
@Profile("db-demo")
@Order(1)
class DemoSandboxSeeder(
@Value("\${spring.datasource.jdbc-url}") private val jdbcUrl: String,
private val dataSource: DataSource,
private val jdbcTemplate: JdbcTemplate,
private val transactionTemplate: TransactionTemplate,
private val userRepository: UserRepository,
private val currencyRepository: CurrencyRepository,
private val uomRepository: UomConversionRepository,
private val shopRepository: ShopRepository,
private val warehouseRepository: WarehouseRepository,
private val itemsRepository: ItemsRepository,
private val itemUomRepository: ItemUomRespository,
private val bomRepository: BomRepository,
private val bomMaterialRepository: BomMaterialRepository,
private val m18DataLogRepository: M18DataLogRepository,
private val purchaseOrderRepository: PurchaseOrderRepository,
private val purchaseOrderLineRepository: PurchaseOrderLineRepository,
private val stockInRepository: StockInRepository,
private val stockInLineRepository: StockInLineRepository,
private val inventoryRepository: InventoryRepository,
private val inventoryLotRepository: InventoryLotRepository,
private val inventoryLotLineRepository: InventoryLotLineRepository,
private val jobOrderRepository: JobOrderRepository,
private val jobOrderBomMaterialRepository: JobOrderBomMaterialRepository,
private val productProcessRepository: ProductProcessRepository,
private val deliveryOrderRepository: DeliveryOrderRepository,
private val deliveryOrderLineRepository: DeliveryOrderLineRepository,
private val pickOrderRepository: PickOrderRepository,
private val pickOrderLineRepository: PickOrderLineRepository,
) : ApplicationRunner {

private val log = LoggerFactory.getLogger(DemoSandboxSeeder::class.java)

override fun run(args: ApplicationArguments) {
val configuredName = databaseName(jdbcUrl)
if (configuredName != DEMO_DATABASE) {
throw IllegalStateException(
"Demo profile refused to start. spring.datasource.jdbc-url database is '$configuredName', expected '$DEMO_DATABASE'.",
)
}
val connectedName = dataSource.connection.use { it.catalog }
if (!DEMO_DATABASE.equals(connectedName, ignoreCase = true)) {
throw IllegalStateException(
"Demo profile refused to start. Connected database is '$connectedName', expected '$DEMO_DATABASE'.",
)
}
refuseIfLiveSchemaReadable()

transactionTemplate.executeWithoutResult {
ensureAuthorities()
val existing = userRepository.findByUsernameAndDeletedFalse(DEMO_USERNAME)
if (existing.isPresent) {
assignDemoAuthorities(existing.get().id)
log.info("Demo sandbox already present (user '{}').", DEMO_USERNAME)
return@executeWithoutResult
}
seed()
log.info("Demo sandbox seeded. Sign in as {} / {}", DEMO_USERNAME, DEMO_PASSWORD)
}
}

/**
* The demo login must not be able to read the live company schema.
* MySQL error 1142/1044 means access denied; 1049 means that schema is not visible.
*/
private fun refuseIfLiveSchemaReadable() {
try {
jdbcTemplate.queryForObject("SELECT 1 FROM fpsmsdb.`user` LIMIT 1", Int::class.java)
} catch (ex: DataAccessException) {
val code = generateSequence<Throwable>(ex) { it.cause }
.filterIsInstance<SQLException>()
.firstOrNull()
?.errorCode
if (code == 1142 || code == 1044 || code == 1049) {
log.info("Demo database login cannot read live schema fpsmsdb (MySQL error {}).", code)
return
}
throw IllegalStateException(
"Demo profile could not prove that live schema fpsmsdb is unreachable (MySQL error $code).",
ex,
)
}
throw IllegalStateException("Demo database login can read the live schema. Refusing to start.")
}

private fun ensureAuthorities() {
val roles = listOf(
"ADMIN" to "行政",
"STOCK" to "倉務",
"PROD" to "出品",
"PACK" to "包裝",
"REPORT_MGMT" to "報告管理",
)
for ((code, name) in roles) {
jdbcTemplate.update(
"""
INSERT INTO authority (authority, name)
SELECT ?, ?
WHERE NOT EXISTS (SELECT 1 FROM authority a WHERE a.authority = ?)
""".trimIndent(),
code,
name,
code,
)
}
}

private fun assignDemoAuthorities(userId: Long?) {
if (userId == null) return
userRepository.assignAuthoritiesByCodesIfNotExists(userId, DEMO_AUTHORITY_CODES)
}

private fun seed() {
val now = LocalDateTime.now()
val today = LocalDate.now()
val user = saveDemoUser()
val currency = currencyRepository.save(Currency().apply {
code = "HKD"
name = "HKD"
description = "港幣"
m18Id = 910001L
m18LastModifyDate = now
})
val kg = uomRepository.save(uom("KG", "公斤", 910011L, now))
val pcs = uomRepository.save(uom("PCS", "件", 910012L, now))
val supplier = shopRepository.save(shop("DEMO-SUP", "北風原料供應", ShopType.SUPPLIER, 910021L, now))
val customer = shopRepository.save(shop("DEMO-SHP", "海港咖啡店", ShopType.SHOP, 910022L, now))
val aisleA = warehouseRepository.save(warehouse("DEMO-A", "1", "甲倉", "示範貨架甲"))
val aisleB = warehouseRepository.save(warehouse("DEMO-B", "2", "乙倉", "示範貨架乙"))

val flour = itemsRepository.save(item("DEMO-RM-01", "普通麵粉", ItemType.MATERIAL.type, 910101L, now))
val milk = itemsRepository.save(item("DEMO-RM-02", "鮮奶", ItemType.MATERIAL.type, 910102L, now))
val dough = itemsRepository.save(item("DEMO-SF-01", "麵團", ItemType.SFG.type, 910103L, now))
val loaf = itemsRepository.save(item("DEMO-FG-01", "鄉村麵包", ItemType.FG.type, 910104L, now))
val bun = itemsRepository.save(item("DEMO-FG-02", "奶包", ItemType.FG.type, 910105L, now))

val flourUom = itemUomRepository.save(itemUom(flour, kg, currency, 910111L, now))
val milkUom = itemUomRepository.save(itemUom(milk, kg, currency, 910112L, now))
itemUomRepository.save(itemUom(dough, kg, currency, 910113L, now))
val loafUom = itemUomRepository.save(itemUom(loaf, pcs, currency, 910114L, now))
itemUomRepository.save(itemUom(bun, pcs, currency, 910115L, now))

val bom = bomRepository.save(Bom().apply {
code = "DEMO-BOM-01"
name = "鄉村麵包"
description = "FG"
type = "FG"
item = loaf
outputQty = BigDecimal("10")
outputQtyUom = "PCS"
status = BomStatus.ACTIVE
m18Id = 910201L
m18LastModifyDate = now
})
bomMaterialRepository.save(bomMaterial(bom, flour, kg, BigDecimal("2"), 910211L, now))
bomMaterialRepository.save(bomMaterial(bom, milk, kg, BigDecimal("1"), 910212L, now))

val dataLog = m18DataLogRepository.save(M18DataLog().apply {
refType = "DEMO"
m18Id = 910001L
status = M18DataLogStatus.SUCCESS
m18LastModifyDate = now
})

val poPending = purchaseOrder("DEMO-PO-001", supplier, customer, currency, dataLog, PurchaseOrderStatus.PENDING, now, null)
purchaseLine(poPending, dough, pcs, dataLog, BigDecimal("20"), PurchaseOrderLineStatus.PENDING)

val poReceiving = purchaseOrder("DEMO-PO-002", supplier, customer, currency, dataLog, PurchaseOrderStatus.RECEIVING, now.minusDays(1), null)
val receivingLine = purchaseLine(poReceiving, flour, kg, dataLog, BigDecimal("25"), PurchaseOrderLineStatus.RECEIVING)
val receivingStockIn = stockIn("DEMO-SI-PO2", supplier, customer, poReceiving, dataLog, "receiving", now.minusDays(1), null)
val receivingSil = stockInLine(
receivingStockIn, flour, receivingLine, null, BigDecimal("25"), BigDecimal("25"),
"received", "DEMO-LOT-FL2", today.plusDays(180), now.minusDays(1),
)
inventoryLotRepository.save(lot(flour, receivingSil, "DEMO-LOT-FL2", today.plusDays(180), now.minusDays(1)))

val poDone = purchaseOrder("DEMO-PO-003", supplier, customer, currency, dataLog, PurchaseOrderStatus.COMPLETED, now.minusDays(3), now.minusDays(1))
val flourLine = purchaseLine(poDone, flour, kg, dataLog, BigDecimal("40"), PurchaseOrderLineStatus.COMPLETED)
val milkLine = purchaseLine(poDone, milk, kg, dataLog, BigDecimal("20"), PurchaseOrderLineStatus.COMPLETED)
val doneStockIn = stockIn("DEMO-SI-PO3", supplier, customer, poDone, dataLog, "completed", now.minusDays(3), now.minusDays(1))
putAway(
doneStockIn, flour, flourLine, null, flourUom, aisleA, currency, kg,
BigDecimal("40"), "DEMO-LOT-FL1", today.plusDays(180), now.minusDays(1),
)
putAway(
doneStockIn, milk, milkLine, null, milkUom, aisleA, currency, kg,
BigDecimal("20"), "DEMO-LOT-MK1", today.plusDays(14), now.minusDays(1),
)

val joPlanning = jobOrder("DEMO-JO-001", bom, JobOrderStatus.PLANNING, now.plusDays(1), null)
jobMaterial(joPlanning, flour, kg, BigDecimal("20"), JobOrderBomMaterialStatus.PENDING)
jobMaterial(joPlanning, milk, kg, BigDecimal("10"), JobOrderBomMaterialStatus.PENDING)

val joProcessing = jobOrder("DEMO-JO-002", bom, JobOrderStatus.PROCESSING, now, now)
jobMaterial(joProcessing, flour, kg, BigDecimal("5"), JobOrderBomMaterialStatus.PENDING)
jobMaterial(joProcessing, milk, kg, BigDecimal("2"), JobOrderBomMaterialStatus.PENDING)
productProcessRepository.save(ProductProcess().apply {
productProcessCode = "DEMO-PP-002"
jobOrder = joProcessing
item = loaf
this.bom = bom
status = ProductProcessStatus.IN_PROGRESS
startTime = now
date = today
productionPriority = 50
submitedBagRecord = false
})
val joPick = pickOrderRepository.save(PickOrder().apply {
code = "DEMO-PK-JO2"
jobOrder = joProcessing
targetDate = now
type = PickOrderType.JOB_ORDER
status = PickOrderStatus.RELEASED
releasedBy = user
releasedDate = now
})
pickLine(joPick, flour, kg, BigDecimal("5"), PickOrderLineStatus.PENDING)
pickLine(joPick, milk, kg, BigDecimal("2"), PickOrderLineStatus.PENDING)

val joStoring = jobOrder("DEMO-JO-003", bom, JobOrderStatus.STORING, now.minusDays(1), now.minusHours(2))
jobMaterial(joStoring, flour, kg, BigDecimal("2"), JobOrderBomMaterialStatus.COMPLETED)
jobMaterial(joStoring, milk, kg, BigDecimal("1"), JobOrderBomMaterialStatus.COMPLETED)
productProcessRepository.save(ProductProcess().apply {
productProcessCode = "DEMO-PP-003"
jobOrder = joStoring
item = loaf
this.bom = bom
status = ProductProcessStatus.COMPLETED
startTime = now.minusHours(4)
endTime = now.minusHours(2)
date = today
productionPriority = 40
submitedBagRecord = false
})
val joStockIn = stockIn("DEMO-SI-JO3", supplier, customer, null, dataLog, "receiving", now.minusHours(2), null)
val joSil = stockInLine(
joStockIn, loaf, null, joStoring, BigDecimal("10"), BigDecimal("10"),
"received", "DEMO-LOT-LF2", today.plusDays(5), now.minusHours(2),
)
inventoryLotRepository.save(lot(loaf, joSil, "DEMO-LOT-LF2", today.plusDays(5), now.minusHours(2)))

val fgStockIn = stockIn("DEMO-SI-FG1", supplier, customer, null, dataLog, "completed", now.minusDays(2), now.minusDays(2))
putAway(
fgStockIn, loaf, null, null, loafUom, aisleB, currency, pcs,
BigDecimal("30"), "DEMO-LOT-LF1", today.plusDays(5), now.minusDays(2),
)

val doPending = deliveryOrder("DEMO-DO-001", customer, currency, dataLog, DeliveryOrderStatus.PENDING, now, null)
deliveryLine(doPending, bun, pcs, dataLog, BigDecimal("12"), DeliveryOrderLineStatus.PENDING)

val doPicking = deliveryOrder("DEMO-DO-002", customer, currency, dataLog, DeliveryOrderStatus.RECEIVING, now, null)
deliveryLine(doPicking, loaf, pcs, dataLog, BigDecimal("4"), DeliveryOrderLineStatus.PICKING)
val doPick = pickOrderRepository.save(PickOrder().apply {
code = "DEMO-PK-DO2"
deliveryOrder = doPicking
targetDate = now
type = PickOrderType.DELIVERY_ORDER
status = PickOrderStatus.RELEASED
releasedBy = user
releasedDate = now
})
pickLine(doPick, loaf, pcs, BigDecimal("4"), PickOrderLineStatus.PENDING)

val doDone = deliveryOrder("DEMO-DO-003", customer, currency, dataLog, DeliveryOrderStatus.COMPLETED, now.minusDays(2), now.minusDays(1))
deliveryLine(doDone, loaf, pcs, dataLog, BigDecimal("6"), DeliveryOrderLineStatus.COMPLETED)
val donePick = pickOrderRepository.save(PickOrder().apply {
code = "DEMO-PK-DO3"
deliveryOrder = doDone
targetDate = now.minusDays(2)
completeDate = now.minusDays(1)
type = PickOrderType.DELIVERY_ORDER
status = PickOrderStatus.COMPLETED
releasedBy = user
releasedDate = now.minusDays(2)
})
pickLine(donePick, loaf, pcs, BigDecimal("6"), PickOrderLineStatus.COMPLETED)
}

private fun saveDemoUser(): User {
val user = userRepository.save(User().apply {
username = DEMO_USERNAME
password = org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder().encode(DEMO_PASSWORD)
locked = false
name = "示範管理員"
title = "Demo"
staffNo = DEMO_USERNAME
lotusNotesUser = false
})
assignDemoAuthorities(user.id)
return user
}

private fun uom(code: String, desc: String, m18Id: Long, now: LocalDateTime) = UomConversion().apply {
this.code = code
udfudesc = desc
udfShortDesc = code
this.m18Id = m18Id
lastModifyDate = now
}

private fun shop(code: String, name: String, type: ShopType, m18Id: Long, now: LocalDateTime) = Shop().apply {
this.code = code
this.name = name
this.type = type
this.m18Id = m18Id
m18LastModifyDate = now
}

private fun warehouse(code: String, orderNo: String, name: String, description: String) = Warehouse().apply {
this.code = code
order = orderNo
this.name = name
this.description = description
capacity = BigDecimal("1000")
store_id = "DEMO"
}

private fun item(code: String, name: String, type: String, m18Id: Long, now: LocalDateTime) = Items().apply {
this.code = code
this.name = name
description = name
this.type = type
shelfLife = 30.0
this.m18Id = m18Id
m18LastModifyDate = now
}

private fun itemUom(item: Items, uom: UomConversion, currency: Currency, m18Id: Long, now: LocalDateTime) = ItemUom().apply {
this.item = item
this.uom = uom
baseUnit = true
stockUnit = true
pickingUnit = true
salesUnit = true
purchaseUnit = true
this.currency = currency
this.m18Id = m18Id
m18LastModifyDate = now
ratioN = BigDecimal.ONE
ratioD = BigDecimal.ONE
}

private fun bomMaterial(bom: Bom, item: Items, uom: UomConversion, qty: BigDecimal, m18Id: Long, now: LocalDateTime) =
BomMaterial().apply {
this.bom = bom
this.item = item
itemName = item.name
isConsumable = false
this.qty = qty
this.uom = uom
uomName = uom.code
baseUnit = Integer.valueOf(uom.m18Id.toInt()) as Integer
stockUnit = Integer.valueOf(uom.m18Id.toInt()) as Integer
this.m18Id = m18Id
m18LastModifyDate = now
}

private fun purchaseOrder(
code: String,
supplier: Shop,
shop: Shop,
currency: Currency,
dataLog: M18DataLog,
status: PurchaseOrderStatus,
orderDate: LocalDateTime,
completeDate: LocalDateTime?,
) = purchaseOrderRepository.save(PurchaseOrder().apply {
this.code = code
this.supplier = supplier
this.shop = shop
this.currency = currency
this.orderDate = orderDate
estimatedArrivalDate = orderDate.plusDays(2)
this.completeDate = completeDate
this.status = status
type = PurchaseOrderType.MATERIAL
m18DataLog = dataLog
m18BeId = 910301L
})

private fun purchaseLine(
po: PurchaseOrder,
item: Items,
uom: UomConversion,
dataLog: M18DataLog,
qty: BigDecimal,
status: PurchaseOrderLineStatus,
) = purchaseOrderLineRepository.save(PurchaseOrderLine().apply {
this.purchaseOrder = po
this.item = item
itemNo = item.code
this.uom = uom
uomM18 = uom
this.qty = qty
qtyM18 = qty
this.status = status
m18DataLog = dataLog
})

private fun stockIn(
code: String,
supplier: Shop,
shop: Shop,
purchaseOrder: PurchaseOrder?,
dataLog: M18DataLog,
status: String,
orderDate: LocalDateTime,
completeDate: LocalDateTime?,
) = stockInRepository.save(StockIn().apply {
this.code = code
this.supplier = supplier
this.shop = shop
this.purchaseOrder = purchaseOrder
m18DataLog = dataLog
this.orderDate = orderDate
this.completeDate = completeDate
this.status = status
})

private fun stockInLine(
stockIn: StockIn,
item: Items,
purchaseOrderLine: PurchaseOrderLine?,
jobOrder: JobOrder?,
demandQty: BigDecimal,
acceptedQty: BigDecimal,
status: String,
lotNo: String,
expiry: LocalDate,
receiptDate: LocalDateTime,
) = stockInLineRepository.save(StockInLine().apply {
this.stockIn = stockIn
this.item = item
itemNo = item.code
this.purchaseOrder = purchaseOrderLine?.purchaseOrder
this.purchaseOrderLine = purchaseOrderLine
this.jobOrder = jobOrder
this.demandQty = demandQty
this.acceptedQty = acceptedQty
this.status = status
this.lotNo = lotNo
expiryDate = expiry
this.receiptDate = receiptDate
})

private fun lot(item: Items, stockInLine: StockInLine, lotNo: String, expiry: LocalDate, stockInDate: LocalDateTime): InventoryLot {
val saved = inventoryLotRepository.save(InventoryLot().apply {
this.item = item
this.stockInLine = stockInLine
this.lotNo = lotNo
expiryDate = expiry
this.stockInDate = stockInDate
productionDate = stockInDate
})
stockInLine.inventoryLot = saved
stockInLineRepository.save(stockInLine)
return saved
}

private fun putAway(
stockIn: StockIn,
item: Items,
purchaseOrderLine: PurchaseOrderLine?,
jobOrder: JobOrder?,
stockUom: ItemUom,
warehouse: Warehouse,
currency: Currency,
uom: UomConversion,
qty: BigDecimal,
lotNo: String,
expiry: LocalDate,
whenAt: LocalDateTime,
) {
val line = stockInLine(
stockIn, item, purchaseOrderLine, jobOrder, qty, qty, "completed", lotNo, expiry, whenAt,
)
val savedLot = lot(item, line, lotNo, expiry, whenAt)
val lotLine = inventoryLotLineRepository.save(InventoryLotLine().apply {
inventoryLot = savedLot
this.warehouse = warehouse
inQty = qty
outQty = BigDecimal.ZERO
holdQty = BigDecimal.ZERO
this.stockUom = stockUom
status = InventoryLotLineStatus.AVAILABLE
})
line.inventoryLotLine = lotLine
stockInLineRepository.save(line)
// Lot-line insert trigger may already write inventory. Add a row only if it did not.
if (inventoryRepository.findFirstByItemIdAndDeletedIsFalseOrderByIdAsc(item.id!!) == null) {
inventoryRepository.save(Inventory().apply {
this.item = item
onHandQty = qty
onHoldQty = BigDecimal.ZERO
unavailableQty = BigDecimal.ZERO
this.uom = uom
this.currency = currency
status = "available"
})
}
}

private fun jobOrder(
code: String,
bom: Bom,
status: JobOrderStatus,
planStart: LocalDateTime,
actualStart: LocalDateTime?,
) = jobOrderRepository.save(JobOrder().apply {
this.code = code
this.bom = bom
isHidden = false
this.planStart = planStart
this.actualStart = actualStart
planEnd = planStart.plusDays(1)
reqQty = BigDecimal("10")
this.status = status
type = "job"
})

private fun jobMaterial(jobOrder: JobOrder, item: Items, uom: UomConversion, qty: BigDecimal, status: JobOrderBomMaterialStatus) {
jobOrderBomMaterialRepository.save(JobOrderBomMaterial().apply {
this.jobOrder = jobOrder
this.item = item
reqQty = qty
this.uom = uom
this.status = status
})
}

private fun deliveryOrder(
code: String,
shop: Shop,
currency: Currency,
dataLog: M18DataLog,
status: DeliveryOrderStatus,
orderDate: LocalDateTime,
completeDate: LocalDateTime?,
) = deliveryOrderRepository.save(DeliveryOrder().apply {
this.code = code
this.shop = shop
this.currency = currency
this.orderDate = orderDate
estimatedArrivalDate = orderDate.plusDays(1)
this.completeDate = completeDate
this.status = status
m18DataLog = dataLog
m18BeId = 910401L
isExtra = false
})

private fun deliveryLine(
deliveryOrder: DeliveryOrder,
item: Items,
uom: UomConversion,
dataLog: M18DataLog,
qty: BigDecimal,
status: DeliveryOrderLineStatus,
) {
deliveryOrderLineRepository.save(DeliveryOrderLine().apply {
this.deliveryOrder = deliveryOrder
this.item = item
itemNo = item.code
this.uom = uom
uomM18 = uom
this.qty = qty
qtyM18 = qty
this.status = status
m18DataLog = dataLog
})
}

private fun pickLine(pickOrder: PickOrder, item: Items, uom: UomConversion, qty: BigDecimal, status: PickOrderLineStatus) {
pickOrderLineRepository.save(PickOrderLine().apply {
this.pickOrder = pickOrder
this.item = item
this.qty = qty
this.uom = uom
this.status = status
})
}

private fun databaseName(jdbcUrl: String): String? {
val path = jdbcUrl.substringBefore('?').substringAfter("://")
val slash = path.indexOf('/')
if (slash < 0 || slash == path.lastIndex) return null
return path.substring(slash + 1).trim().ifEmpty { null }
}

companion object {
const val DEMO_DATABASE = "fpsms_demo"
const val DEMO_USERNAME = "demo"
const val DEMO_PASSWORD = "Demo2026!"
val DEMO_AUTHORITY_CODES = listOf("ADMIN", "STOCK", "PROD", "PACK", "REPORT_MGMT")
}
}

+ 18
- 18
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt Переглянути файл

@@ -504,10 +504,10 @@ open class DeliveryOrderService(
NULLIF(TRIM(dop.handlerName), ''),
NULLIF(TRIM(u.name), '')
) AS handler_name
FROM fpsmsdb.delivery_order_pick_order dop
INNER JOIN fpsmsdb.pick_order po
FROM delivery_order_pick_order dop
INNER JOIN pick_order po
ON po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0
LEFT JOIN fpsmsdb.user u
LEFT JOIN user u
ON u.id = dop.handledBy AND u.deleted = 0
WHERE dop.deleted = 0
AND po.doId = :doId
@@ -518,12 +518,12 @@ open class DeliveryOrderService(
NULLIF(TRIM(dop.handlerName), ''),
NULLIF(TRIM(u.name), '')
) AS handler_name
FROM fpsmsdb.delivery_order_pick_order dop
INNER JOIN fpsmsdb.pick_order po
FROM delivery_order_pick_order dop
INNER JOIN pick_order po
ON po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0
INNER JOIN fpsmsdb.do_pick_order_line dpol
INNER JOIN do_pick_order_line dpol
ON dpol.pick_order_id = po.id AND dpol.deleted = 0
LEFT JOIN fpsmsdb.user u
LEFT JOIN user u
ON u.id = dop.handledBy AND u.deleted = 0
WHERE dop.deleted = 0
AND dpol.do_order_id = :doId
@@ -558,8 +558,8 @@ open class DeliveryOrderService(
SELECT DISTINCT TRIM(truck_lance_code) AS truck_lance_code
FROM (
SELECT NULLIF(TRIM(dop.truckLanceCode), '') AS truck_lance_code
FROM fpsmsdb.delivery_order_pick_order dop
INNER JOIN fpsmsdb.pick_order po
FROM delivery_order_pick_order dop
INNER JOIN pick_order po
ON po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0
WHERE dop.deleted = 0
AND po.doId = :doId
@@ -567,10 +567,10 @@ open class DeliveryOrderService(
UNION

SELECT NULLIF(TRIM(dop.truckLanceCode), '') AS truck_lance_code
FROM fpsmsdb.delivery_order_pick_order dop
INNER JOIN fpsmsdb.pick_order po
FROM delivery_order_pick_order dop
INNER JOIN pick_order po
ON po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0
INNER JOIN fpsmsdb.do_pick_order_line dpol
INNER JOIN do_pick_order_line dpol
ON dpol.pick_order_id = po.id AND dpol.deleted = 0
WHERE dop.deleted = 0
AND dpol.do_order_id = :doId
@@ -837,8 +837,8 @@ open class DeliveryOrderService(
// 查询 warehouse 的 order 字段
val sql = """
SELECT w.`order` as warehouseOrder
FROM fpsmsdb.inventory_lot_line ill
JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
FROM inventory_lot_line ill
JOIN warehouse w ON w.id = ill.warehouseId
WHERE ill.inventoryLotId = :lotId
AND ill.deleted = false
ORDER BY w.`order` ASC
@@ -886,8 +886,8 @@ open class DeliveryOrderService(
return inventoryLotId?.let { lotId ->
val sql = """
SELECT w.code as warehouseCode
FROM fpsmsdb.inventory_lot_line ill
JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
FROM inventory_lot_line ill
JOIN warehouse w ON w.id = ill.warehouseId
WHERE ill.inventoryLotId = :lotId
AND ill.deleted = false
ORDER BY w.`order` ASC
@@ -2610,8 +2610,8 @@ val inventoryLotLine = illId?.let { inventoryLotLineMap[it] }
COUNT(DISTINCT CASE WHEN i.store_id = '4F' THEN dol.itemId END) AS count_4f,
COUNT(DISTINCT CASE WHEN i.store_id = '2F' THEN dol.itemId END) AS count_2f,
COUNT(DISTINCT dol.itemId) AS total_items
FROM fpsmsdb.delivery_order_line dol
INNER JOIN fpsmsdb.items i ON i.id = dol.itemId AND i.deleted = 0
FROM delivery_order_line dol
INNER JOIN items i ON i.id = dol.itemId AND i.deleted = 0
WHERE dol.deliveryOrderId = :doId AND dol.deleted = 0
""".trimIndent()



+ 2
- 2
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderAssignmentService.kt Переглянути файл

@@ -166,7 +166,7 @@ class DoPickOrderAssignmentService(
return try {
val totalLinesSql = """
SELECT COUNT(*) as total_lines
FROM fpsmsdb.do_pick_order_line dpol
FROM do_pick_order_line dpol
WHERE dpol.do_pick_order_id = :doPickOrderId
AND dpol.deleted = 0
""".trimIndent()
@@ -180,7 +180,7 @@ class DoPickOrderAssignmentService(
val nonIssueLinesSql = """
SELECT COUNT(*) as non_issue_lines
FROM fpsmsdb.do_pick_order_line dpol
FROM do_pick_order_line dpol
WHERE dpol.do_pick_order_id = :doPickOrderId
AND dpol.deleted = 0
AND (dpol.status IS NULL OR dpol.status != 'issue')


+ 5
- 5
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderQueryService.kt Переглянути файл

@@ -171,7 +171,7 @@ val timeGroups = groupedByTime
return try {
val totalLinesSql = """
SELECT COUNT(*) as total_lines
FROM fpsmsdb.do_pick_order_line dpol
FROM do_pick_order_line dpol
WHERE dpol.do_pick_order_id = :doPickOrderId
AND dpol.deleted = 0
""".trimIndent()
@@ -185,7 +185,7 @@ val timeGroups = groupedByTime
val nonIssueLinesSql = """
SELECT COUNT(*) as non_issue_lines
FROM fpsmsdb.do_pick_order_line dpol
FROM do_pick_order_line dpol
WHERE dpol.do_pick_order_id = :doPickOrderId
AND dpol.deleted = 0
AND (dpol.status IS NULL OR dpol.status != 'issue')
@@ -207,7 +207,7 @@ val timeGroups = groupedByTime
// First get the record_id from do_pick_order_record
val recordSql = """
SELECT record_id
FROM fpsmsdb.do_pick_order_record dpor
FROM do_pick_order_record dpor
WHERE dpor.id = :recordId
""".trimIndent()
@@ -221,7 +221,7 @@ val timeGroups = groupedByTime
// Check do_pick_order_line_record table
val totalLinesSql = """
SELECT COUNT(*) as total_lines
FROM fpsmsdb.do_pick_order_line_record dpolr
FROM do_pick_order_line_record dpolr
WHERE dpolr.record_id = :recordId
AND dpolr.deleted = 0
""".trimIndent()
@@ -235,7 +235,7 @@ val timeGroups = groupedByTime
val nonIssueLinesSql = """
SELECT COUNT(*) as non_issue_lines
FROM fpsmsdb.do_pick_order_line_record dpolr
FROM do_pick_order_line_record dpolr
WHERE dpolr.record_id = :recordId
AND dpolr.deleted = 0
AND (dpolr.status IS NULL OR dpolr.status != 'issue')


+ 9
- 9
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoPickOrderService.kt Переглянути файл

@@ -476,7 +476,7 @@ open class DoPickOrderService(
// 1. 获取该 do_pick_order 的所有 do_pick_order_line 数量
val totalLinesSql = """
SELECT COUNT(*) as total_lines
FROM fpsmsdb.do_pick_order_line dpol
FROM do_pick_order_line dpol
WHERE dpol.do_pick_order_id = :doPickOrderId
AND dpol.deleted = 0
""".trimIndent()
@@ -491,7 +491,7 @@ open class DoPickOrderService(
// 2. 获取非 "issue" 状态的 lines 数量
val nonIssueLinesSql = """
SELECT COUNT(*) as non_issue_lines
FROM fpsmsdb.do_pick_order_line dpol
FROM do_pick_order_line dpol
WHERE dpol.do_pick_order_id = :doPickOrderId
AND dpol.deleted = 0
AND (dpol.status IS NULL OR dpol.status != 'issue')
@@ -609,10 +609,10 @@ open class DoPickOrderService(
dol.deliveryOrderId,
w.store_id,
SUM(ill.inQty - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) AS total_inventory
FROM fpsmsdb.delivery_order_line dol
LEFT JOIN fpsmsdb.inventory_lot il ON il.itemId = dol.itemId AND il.deleted = 0
LEFT JOIN fpsmsdb.inventory_lot_line ill ON ill.inventoryLotId = il.id AND ill.deleted = 0
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId AND w.deleted = 0
FROM delivery_order_line dol
LEFT JOIN inventory_lot il ON il.itemId = dol.itemId AND il.deleted = 0
LEFT JOIN inventory_lot_line ill ON ill.inventoryLotId = il.id AND ill.deleted = 0
LEFT JOIN warehouse w ON w.id = ill.warehouseId AND w.deleted = 0
WHERE dol.deleted = 0 AND dol.deliveryOrderId = :doOrderId
GROUP BY dol.deliveryOrderId, w.store_id
ORDER BY total_inventory DESC
@@ -863,13 +863,13 @@ open class DoPickOrderService(
MIN(dop.ticketReleaseTime) AS firstTicketStartTime,
SUM(CASE WHEN dop.ticketCompleteDateTime IS NOT NULL THEN 1 ELSE 0 END) AS numberOfTicketsCompleted,
MAX(dop.ticketCompleteDateTime) AS lastTicketEndTime
FROM fpsmsdb.delivery_order_pick_order dop
FROM delivery_order_pick_order dop
LEFT JOIN (
SELECT
po.deliveryOrderPickOrderId AS dopId,
COUNT(pol.id) AS cnt
FROM fpsmsdb.pick_order po
INNER JOIN fpsmsdb.pick_order_line pol
FROM pick_order po
INNER JOIN pick_order_line pol
ON pol.poId = po.id
AND pol.deleted = 0
WHERE po.deleted = 0


+ 52
- 52
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoReleaseCoordinatorService.kt Переглянути файл

@@ -145,15 +145,15 @@ class DoReleaseCoordinatorService(
val dayOfWeekSql = getDayOfWeekAbbrSql("do.estimatedArrivalDate")
val pfCases = doFloorSupplierSettingsService.sqlPreferredFloorCases("s.code")
val updateSql = """
UPDATE fpsmsdb.do_pick_order dpo
UPDATE do_pick_order dpo
INNER JOIN (
WITH PreferredFloor AS (
SELECT
do.id AS deliveryOrderId,
${pfCases.floorStringCase} AS preferred_floor,
${pfCases.storeIdNumericCase} AS preferred_store_id
FROM fpsmsdb.delivery_order do
LEFT JOIN fpsmsdb.shop s ON s.id = do.supplierId AND s.deleted = 0
FROM delivery_order do
LEFT JOIN shop s ON s.id = do.supplierId AND s.deleted = 0
WHERE do.deleted = 0
),
TruckSelection AS (
@@ -163,102 +163,102 @@ class DoReleaseCoordinatorService(
pf.preferred_floor,
$dayOfWeekSql AS day_of_week_abbr,
CASE
WHEN (SELECT COUNT(*) FROM fpsmsdb.truck t WHERE t.shopId = do.shopId AND t.deleted = 0 AND t.Store_id = pf.preferred_store_id) > 1
WHEN (SELECT COUNT(*) FROM truck t WHERE t.shopId = do.shopId AND t.deleted = 0 AND t.Store_id = pf.preferred_store_id) > 1
AND pf.preferred_store_id = 4
AND do.estimatedArrivalDate IS NOT NULL THEN
COALESCE(
(SELECT t.DepartureTime FROM fpsmsdb.truck t
(SELECT t.DepartureTime FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
AND t.Store_id = pf.preferred_store_id
AND t.TruckLanceCode LIKE CONCAT('%', $dayOfWeekSql, '%')
ORDER BY t.DepartureTime ASC LIMIT 1),
(SELECT t.DepartureTime FROM fpsmsdb.truck t
(SELECT t.DepartureTime FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
AND t.Store_id = pf.preferred_store_id
ORDER BY t.DepartureTime ASC LIMIT 1),
(SELECT t.DepartureTime FROM fpsmsdb.truck t
(SELECT t.DepartureTime FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
ORDER BY t.DepartureTime ASC LIMIT 1),
'23:59:59'
)
WHEN (SELECT COUNT(*) FROM fpsmsdb.truck t WHERE t.shopId = do.shopId AND t.deleted = 0) > 1 THEN
WHEN (SELECT COUNT(*) FROM truck t WHERE t.shopId = do.shopId AND t.deleted = 0) > 1 THEN
COALESCE(
(SELECT t.DepartureTime FROM fpsmsdb.truck t
(SELECT t.DepartureTime FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
AND t.Store_id = pf.preferred_store_id
ORDER BY t.DepartureTime ASC LIMIT 1),
(SELECT t.DepartureTime FROM fpsmsdb.truck t
(SELECT t.DepartureTime FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
ORDER BY t.DepartureTime ASC LIMIT 1),
'23:59:59'
)
ELSE
COALESCE(
(SELECT t.DepartureTime FROM fpsmsdb.truck t
(SELECT t.DepartureTime FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
ORDER BY t.DepartureTime ASC LIMIT 1),
'23:59:59'
)
END AS selected_departure_time,
CASE
WHEN (SELECT COUNT(*) FROM fpsmsdb.truck t WHERE t.shopId = do.shopId AND t.deleted = 0 AND t.Store_id = pf.preferred_store_id) > 1
WHEN (SELECT COUNT(*) FROM truck t WHERE t.shopId = do.shopId AND t.deleted = 0 AND t.Store_id = pf.preferred_store_id) > 1
AND pf.preferred_store_id = 4
AND do.estimatedArrivalDate IS NOT NULL THEN
COALESCE(
(SELECT t.TruckLanceCode FROM fpsmsdb.truck t
(SELECT t.TruckLanceCode FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
AND t.Store_id = pf.preferred_store_id
AND t.TruckLanceCode LIKE CONCAT('%', $dayOfWeekSql, '%')
ORDER BY t.DepartureTime ASC LIMIT 1),
(SELECT t.TruckLanceCode FROM fpsmsdb.truck t
(SELECT t.TruckLanceCode FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
AND t.Store_id = pf.preferred_store_id
ORDER BY t.DepartureTime ASC LIMIT 1),
(SELECT t.TruckLanceCode FROM fpsmsdb.truck t
(SELECT t.TruckLanceCode FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
ORDER BY t.DepartureTime ASC LIMIT 1),
'ZZ'
)
WHEN (SELECT COUNT(*) FROM fpsmsdb.truck t WHERE t.shopId = do.shopId AND t.deleted = 0) > 1 THEN
WHEN (SELECT COUNT(*) FROM truck t WHERE t.shopId = do.shopId AND t.deleted = 0) > 1 THEN
COALESCE(
(SELECT t.TruckLanceCode FROM fpsmsdb.truck t
(SELECT t.TruckLanceCode FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
AND t.Store_id = pf.preferred_store_id
ORDER BY t.DepartureTime ASC LIMIT 1),
(SELECT t.TruckLanceCode FROM fpsmsdb.truck t
(SELECT t.TruckLanceCode FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
ORDER BY t.DepartureTime ASC LIMIT 1),
'ZZ'
)
ELSE
COALESCE(
(SELECT t.TruckLanceCode FROM fpsmsdb.truck t
(SELECT t.TruckLanceCode FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
ORDER BY t.DepartureTime ASC LIMIT 1),
'ZZ'
)
END AS selected_truck_lance,
COALESCE(
(SELECT t.LoadingSequence FROM fpsmsdb.truck t
(SELECT t.LoadingSequence FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
AND t.Store_id = pf.preferred_store_id
AND (pf.preferred_store_id != 4 OR do.estimatedArrivalDate IS NULL OR
t.TruckLanceCode LIKE CONCAT('%', $dayOfWeekSql, '%'))
ORDER BY t.DepartureTime ASC LIMIT 1),
(SELECT t.LoadingSequence FROM fpsmsdb.truck t
(SELECT t.LoadingSequence FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
AND t.Store_id = pf.preferred_store_id
ORDER BY t.DepartureTime ASC LIMIT 1),
(SELECT t.LoadingSequence FROM fpsmsdb.truck t
(SELECT t.LoadingSequence FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
ORDER BY t.DepartureTime ASC LIMIT 1),
999
) AS loading_sequence
FROM fpsmsdb.delivery_order do
FROM delivery_order do
LEFT JOIN PreferredFloor pf ON pf.deliveryOrderId = do.id
WHERE do.id IN (
SELECT DISTINCT do_order_id
FROM fpsmsdb.do_pick_order
FROM do_pick_order
WHERE ticket_no LIKE 'TEMP-%'
AND deleted = 0
)
@@ -283,7 +283,7 @@ class DoReleaseCoordinatorService(
dpo2.do_order_id
), 3, '0')
) AS new_ticket_no
FROM fpsmsdb.do_pick_order dpo2
FROM do_pick_order dpo2
LEFT JOIN TruckSelection ts ON ts.delivery_order_id = dpo2.do_order_id
WHERE dpo2.ticket_no LIKE 'TEMP-%'
AND dpo2.deleted = 0
@@ -310,8 +310,8 @@ class DoReleaseCoordinatorService(
do.id AS deliveryOrderId,
${pfCases.floorStringCase} AS preferred_floor,
${pfCases.storeIdNumericCase} AS preferred_store_id
FROM fpsmsdb.delivery_order do
LEFT JOIN fpsmsdb.shop s ON s.id = do.supplierId AND s.deleted = 0
FROM delivery_order do
LEFT JOIN shop s ON s.id = do.supplierId AND s.deleted = 0
WHERE do.id IN (${ids.joinToString(",")})
AND do.deleted = 0
),
@@ -324,12 +324,12 @@ class DoReleaseCoordinatorService(
pf.preferred_store_id,
$dayOfWeekSql AS day_of_week_abbr,
CASE
WHEN (SELECT COUNT(*) FROM fpsmsdb.truck t WHERE t.shopId = do.shopId AND t.deleted = 0 AND t.Store_id = pf.preferred_store_id) > 1
WHEN (SELECT COUNT(*) FROM truck t WHERE t.shopId = do.shopId AND t.deleted = 0 AND t.Store_id = pf.preferred_store_id) > 1
AND pf.preferred_store_id = 4
AND do.estimatedArrivalDate IS NOT NULL THEN
COALESCE(
(SELECT t.DepartureTime
FROM fpsmsdb.truck t
FROM truck t
WHERE t.shopId = do.shopId
AND t.deleted = 0
AND t.Store_id = pf.preferred_store_id
@@ -337,28 +337,28 @@ class DoReleaseCoordinatorService(
ORDER BY t.DepartureTime ASC
LIMIT 1),
(SELECT t.DepartureTime
FROM fpsmsdb.truck t
FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
AND t.Store_id = pf.preferred_store_id
ORDER BY t.DepartureTime ASC
LIMIT 1),
(SELECT t.DepartureTime
FROM fpsmsdb.truck t
FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
ORDER BY t.DepartureTime ASC
LIMIT 1),
'23:59:59'
)
WHEN (SELECT COUNT(*) FROM fpsmsdb.truck t WHERE t.shopId = do.shopId AND t.deleted = 0) > 1 THEN
WHEN (SELECT COUNT(*) FROM truck t WHERE t.shopId = do.shopId AND t.deleted = 0) > 1 THEN
COALESCE(
(SELECT t.DepartureTime
FROM fpsmsdb.truck t
FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
AND t.Store_id = pf.preferred_store_id
ORDER BY t.DepartureTime ASC
LIMIT 1),
(SELECT t.DepartureTime
FROM fpsmsdb.truck t
FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
ORDER BY t.DepartureTime ASC
LIMIT 1),
@@ -367,7 +367,7 @@ class DoReleaseCoordinatorService(
ELSE
COALESCE(
(SELECT t.DepartureTime
FROM fpsmsdb.truck t
FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
ORDER BY t.DepartureTime ASC
LIMIT 1),
@@ -375,12 +375,12 @@ class DoReleaseCoordinatorService(
)
END AS selected_departure_time,
CASE
WHEN (SELECT COUNT(*) FROM fpsmsdb.truck t WHERE t.shopId = do.shopId AND t.deleted = 0 AND t.Store_id = pf.preferred_store_id) > 1
WHEN (SELECT COUNT(*) FROM truck t WHERE t.shopId = do.shopId AND t.deleted = 0 AND t.Store_id = pf.preferred_store_id) > 1
AND pf.preferred_store_id = 4
AND do.estimatedArrivalDate IS NOT NULL THEN
COALESCE(
(SELECT t.TruckLanceCode
FROM fpsmsdb.truck t
FROM truck t
WHERE t.shopId = do.shopId
AND t.deleted = 0
AND t.Store_id = pf.preferred_store_id
@@ -388,28 +388,28 @@ class DoReleaseCoordinatorService(
ORDER BY t.DepartureTime ASC
LIMIT 1),
(SELECT t.TruckLanceCode
FROM fpsmsdb.truck t
FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
AND t.Store_id = pf.preferred_store_id
ORDER BY t.DepartureTime ASC
LIMIT 1),
(SELECT t.TruckLanceCode
FROM fpsmsdb.truck t
FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
ORDER BY t.DepartureTime ASC
LIMIT 1),
'ZZ'
)
WHEN (SELECT COUNT(*) FROM fpsmsdb.truck t WHERE t.shopId = do.shopId AND t.deleted = 0) > 1 THEN
WHEN (SELECT COUNT(*) FROM truck t WHERE t.shopId = do.shopId AND t.deleted = 0) > 1 THEN
COALESCE(
(SELECT t.TruckLanceCode
FROM fpsmsdb.truck t
FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
AND t.Store_id = pf.preferred_store_id
ORDER BY t.DepartureTime ASC
LIMIT 1),
(SELECT t.TruckLanceCode
FROM fpsmsdb.truck t
FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
ORDER BY t.DepartureTime ASC
LIMIT 1),
@@ -418,7 +418,7 @@ class DoReleaseCoordinatorService(
ELSE
COALESCE(
(SELECT t.TruckLanceCode
FROM fpsmsdb.truck t
FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
ORDER BY t.DepartureTime ASC
LIMIT 1),
@@ -427,24 +427,24 @@ class DoReleaseCoordinatorService(
END AS selected_truck_lance,
COALESCE(
(SELECT t.LoadingSequence
FROM fpsmsdb.truck t
FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
AND t.Store_id = pf.preferred_store_id
AND (pf.preferred_store_id != 4 OR do.estimatedArrivalDate IS NULL OR
t.TruckLanceCode LIKE CONCAT('%', $dayOfWeekSql, '%'))
ORDER BY t.DepartureTime ASC LIMIT 1),
(SELECT t.LoadingSequence
FROM fpsmsdb.truck t
FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
AND t.Store_id = pf.preferred_store_id
ORDER BY t.DepartureTime ASC LIMIT 1),
(SELECT t.LoadingSequence
FROM fpsmsdb.truck t
FROM truck t
WHERE t.shopId = do.shopId AND t.deleted = 0
ORDER BY t.DepartureTime ASC LIMIT 1),
999
) AS loading_sequence
FROM fpsmsdb.delivery_order do
FROM delivery_order do
LEFT JOIN PreferredFloor pf ON pf.deliveryOrderId = do.id
WHERE do.id IN (${ids.joinToString(",")})
AND do.deleted = 0
@@ -798,7 +798,7 @@ class DoReleaseCoordinatorService(
// 1. 获取 do_pick_order 的所有 pick orders 数量
val totalPickOrdersSql = """
SELECT COUNT(*) as total_pick_orders
FROM fpsmsdb.do_pick_order_line dpol
FROM do_pick_order_line dpol
WHERE dpol.do_pick_order_id = :doPickOrderId
AND dpol.deleted = 0
""".trimIndent()
@@ -813,8 +813,8 @@ class DoReleaseCoordinatorService(
// 2. 获取有库存问题的 pick orders 数量(通过检查 pick_execution_issue 表)
val issuePickOrdersSql = """
SELECT COUNT(DISTINCT dpol.pick_order_id) as issue_pick_orders
FROM fpsmsdb.do_pick_order_line dpol
INNER JOIN fpsmsdb.pick_execution_issue pei ON pei.pick_order_id = dpol.pick_order_id
FROM do_pick_order_line dpol
INNER JOIN pick_execution_issue pei ON pei.pick_order_id = dpol.pick_order_id
WHERE dpol.do_pick_order_id = :doPickOrderId
AND pei.deleted = 0
AND dpol.deleted = 0
@@ -1153,7 +1153,7 @@ class DoReleaseCoordinatorService(
return try {
val totalLinesSql = """
SELECT COUNT(*) as total_lines
FROM fpsmsdb.do_pick_order_line dpol
FROM do_pick_order_line dpol
WHERE dpol.do_pick_order_id = :doPickOrderId
AND dpol.deleted = 0
""".trimIndent()
@@ -1162,7 +1162,7 @@ class DoReleaseCoordinatorService(
if (totalLines == 0) return true
val nonIssueLinesSql = """
SELECT COUNT(*) as non_issue_lines
FROM fpsmsdb.do_pick_order_line dpol
FROM do_pick_order_line dpol
WHERE dpol.do_pick_order_id = :doPickOrderId
AND dpol.deleted = 0
AND (dpol.status IS NULL OR dpol.status != 'issue')


+ 1
- 1
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoReplenishmentService.kt Переглянути файл

@@ -347,7 +347,7 @@ open class DoReplenishmentService(
return jdbcDao.queryForList(
"""
SELECT deliveryOrderPickOrderId AS dopoId
FROM fpsmsdb.pick_order
FROM pick_order
WHERE id = :pickOrderId AND deleted = 0
""".trimIndent(),
mapOf("pickOrderId" to pickOrderId),


+ 13
- 13
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchDopoAssignmentService.kt Переглянути файл

@@ -36,7 +36,7 @@ open class DoWorkbenchDopoAssignmentService(
val headerRows = jdbcDao.queryForList(
"""
SELECT id, ticketStatus
FROM fpsmsdb.delivery_order_pick_order
FROM delivery_order_pick_order
WHERE id = :id AND deleted = 0
""".trimIndent(),
mapOf("id" to request.deliveryOrderPickOrderId)
@@ -71,7 +71,7 @@ open class DoWorkbenchDopoAssignmentService(
val headerRows = jdbcDao.queryForList(
"""
SELECT id, ticketStatus
FROM fpsmsdb.delivery_order_pick_order
FROM delivery_order_pick_order
WHERE id = :id AND deleted = 0
""".trimIndent(),
mapOf("id" to request.deliveryOrderPickOrderId)
@@ -121,14 +121,14 @@ open class DoWorkbenchDopoAssignmentService(
val sql = StringBuilder(
"""
SELECT dop.id
FROM fpsmsdb.delivery_order_pick_order dop
FROM delivery_order_pick_order dop
WHERE dop.deleted = 0
AND dop.storeId = :storeId
AND dop.truckLanceCode = :lance
AND dop.ticketStatus IN ('pending', 'released')
AND dop.handledBy IS NULL
AND EXISTS (
SELECT 1 FROM fpsmsdb.pick_order po
SELECT 1 FROM pick_order po
WHERE po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0 AND po.assignTo IS NULL
)
""".trimIndent()
@@ -233,13 +233,13 @@ open class DoWorkbenchDopoAssignmentService(
val sql = StringBuilder(
"""
SELECT dop.id
FROM fpsmsdb.delivery_order_pick_order dop
FROM delivery_order_pick_order dop
WHERE dop.deleted = 0
AND dop.storeId = :storeId
AND dop.truckLanceCode = :lance
AND dop.ticketStatus IN ('pending', 'released')
AND EXISTS (
SELECT 1 FROM fpsmsdb.pick_order po
SELECT 1 FROM pick_order po
WHERE po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0 AND po.assignTo IS NULL
)
""".trimIndent()
@@ -337,7 +337,7 @@ open class DoWorkbenchDopoAssignmentService(
val (sql, failCode, failMsg) = when (mode) {
AssignMode.ATOMIC -> Triple(
"""
UPDATE fpsmsdb.delivery_order_pick_order
UPDATE delivery_order_pick_order
SET handledBy = :handledBy,
handlerName = :handlerName,
ticketStatus = 'released',
@@ -353,7 +353,7 @@ open class DoWorkbenchDopoAssignmentService(
)
AssignMode.LEGACY_V1 -> Triple(
"""
UPDATE fpsmsdb.delivery_order_pick_order
UPDATE delivery_order_pick_order
SET handledBy = :handledBy,
handlerName = :handlerName,
ticketStatus = 'released',
@@ -402,7 +402,7 @@ open class DoWorkbenchDopoAssignmentService(
// Suggestion store scope: use DOPO.storeId when set; Truck X (null) → 2/F or 4/F from DO suppliers.
val storeIdRow = jdbcDao.queryForList(
"SELECT storeId FROM fpsmsdb.delivery_order_pick_order WHERE id = :id AND deleted = 0",
"SELECT storeId FROM delivery_order_pick_order WHERE id = :id AND deleted = 0",
mapOf("id" to dopoId),
).firstOrNull()
val storeIdKey = storeIdRow?.keys?.find { it.equals("storeId", true) }
@@ -419,7 +419,7 @@ open class DoWorkbenchDopoAssignmentService(
}
val ticketNoRow = jdbcDao.queryForList(
"SELECT ticketNo FROM fpsmsdb.delivery_order_pick_order WHERE id = :id AND deleted = 0",
"SELECT ticketNo FROM delivery_order_pick_order WHERE id = :id AND deleted = 0",
mapOf("id" to dopoId)
).firstOrNull()
val ticketNoKey = ticketNoRow?.keys?.find { it.equals("ticketNo", true) }
@@ -453,9 +453,9 @@ open class DoWorkbenchDopoAssignmentService(
jdbcDao.queryForList(
"""
SELECT DISTINCT s.code AS supplierCode
FROM fpsmsdb.pick_order po
INNER JOIN fpsmsdb.delivery_order d ON d.id = po.doId AND d.deleted = 0
LEFT JOIN fpsmsdb.shop s ON s.id = d.supplierId AND s.deleted = 0
FROM pick_order po
INNER JOIN delivery_order d ON d.id = po.doId AND d.deleted = 0
LEFT JOIN shop s ON s.id = d.supplierId AND s.deleted = 0
WHERE po.deliveryOrderPickOrderId = :dopoId AND po.deleted = 0
""".trimIndent(),
mapOf("dopoId" to dopoId),


+ 67
- 67
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchMainService.kt Переглянути файл

@@ -719,13 +719,13 @@ return MessageResponse(
ORDER BY dop.handlerName
SEPARATOR ', '
) AS handler_names
FROM fpsmsdb.delivery_order_pick_order dop
FROM delivery_order_pick_order dop
WHERE dop.deleted = 0
AND dop.storeId = :storeId
AND dop.requiredDeliveryDate = :requiredDate
AND dop.ticketStatus IN ('pending', 'released')
AND EXISTS (
SELECT 1 FROM fpsmsdb.pick_order po
SELECT 1 FROM pick_order po
WHERE po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0
)
$releaseFilterClause
@@ -861,14 +861,14 @@ return MessageResponse(
val truckXFloorClause = truckXPreferredFloorExistsSql(truckXFloorKey)
val defaultCountSql = """
SELECT COUNT(DISTINCT dop.id) AS c
FROM fpsmsdb.delivery_order_pick_order dop
FROM delivery_order_pick_order dop
WHERE dop.deleted = 0
AND dop.requiredDeliveryDate = :requiredDate
AND dop.ticketStatus IN ('pending', 'released')
AND (dop.storeId IS NULL OR TRIM(dop.storeId) = '')
AND dop.truckLanceCode = '車線-X'
AND EXISTS (
SELECT 1 FROM fpsmsdb.pick_order po
SELECT 1 FROM pick_order po
WHERE po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0
)
$truckXFloorClause
@@ -939,34 +939,34 @@ return MessageResponse(
THEN CASE
WHEN EXISTS (
SELECT 1
FROM fpsmsdb.pick_order po
INNER JOIN fpsmsdb.delivery_order d ON d.id = po.doId AND d.deleted = 0
LEFT JOIN fpsmsdb.shop s ON s.id = d.supplierId AND s.deleted = 0
FROM pick_order po
INNER JOIN delivery_order d ON d.id = po.doId AND d.deleted = 0
LEFT JOIN shop s ON s.id = d.supplierId AND s.deleted = 0
WHERE po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0
AND ($floorExpr) = '4F'
)
AND NOT EXISTS (
SELECT 1
FROM fpsmsdb.pick_order po2
INNER JOIN fpsmsdb.delivery_order d2 ON d2.id = po2.doId AND d2.deleted = 0
LEFT JOIN fpsmsdb.shop s2 ON s2.id = d2.supplierId AND s2.deleted = 0
FROM pick_order po2
INNER JOIN delivery_order d2 ON d2.id = po2.doId AND d2.deleted = 0
LEFT JOIN shop s2 ON s2.id = d2.supplierId AND s2.deleted = 0
WHERE po2.deliveryOrderPickOrderId = dop.id AND po2.deleted = 0
AND (CASE WHEN s2.code IN ($in4) THEN '4F' ELSE '2F' END) <> '4F'
)
THEN '4/F'
WHEN EXISTS (
SELECT 1
FROM fpsmsdb.pick_order po
INNER JOIN fpsmsdb.delivery_order d ON d.id = po.doId AND d.deleted = 0
LEFT JOIN fpsmsdb.shop s ON s.id = d.supplierId AND s.deleted = 0
FROM pick_order po
INNER JOIN delivery_order d ON d.id = po.doId AND d.deleted = 0
LEFT JOIN shop s ON s.id = d.supplierId AND s.deleted = 0
WHERE po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0
AND ($floorExpr) = '2F'
)
AND NOT EXISTS (
SELECT 1
FROM fpsmsdb.pick_order po2
INNER JOIN fpsmsdb.delivery_order d2 ON d2.id = po2.doId AND d2.deleted = 0
LEFT JOIN fpsmsdb.shop s2 ON s2.id = d2.supplierId AND s2.deleted = 0
FROM pick_order po2
INNER JOIN delivery_order d2 ON d2.id = po2.doId AND d2.deleted = 0
LEFT JOIN shop s2 ON s2.id = d2.supplierId AND s2.deleted = 0
WHERE po2.deliveryOrderPickOrderId = dop.id AND po2.deleted = 0
AND (CASE WHEN s2.code IN ($in4) THEN '4F' ELSE '2F' END) <> '2F'
)
@@ -975,13 +975,13 @@ return MessageResponse(
END
ELSE dop.storeId
END AS displayStoreId
FROM fpsmsdb.delivery_order_pick_order dop
FROM delivery_order_pick_order dop
WHERE dop.deleted = 0
$etraReleaseFilter
AND dop.requiredDeliveryDate = :requiredDate
AND dop.ticketStatus IN ('pending', 'released')
AND EXISTS (
SELECT 1 FROM fpsmsdb.pick_order po
SELECT 1 FROM pick_order po
WHERE po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0
)
) base
@@ -1193,11 +1193,11 @@ return MessageResponse(
GROUP_CONCAT(DISTINCT d.id ORDER BY d.id SEPARATOR ',') AS deliveryOrderIdsStr,
GROUP_CONCAT(DISTINCT d.code ORDER BY d.code SEPARATOR ',') AS deliveryNosStr,
MIN(po.consoCode) AS consoCode
FROM fpsmsdb.delivery_order_pick_order dop
INNER JOIN fpsmsdb.pick_order po
FROM delivery_order_pick_order dop
INNER JOIN pick_order po
ON po.deliveryOrderPickOrderId = dop.id
AND po.deleted = 0
LEFT JOIN fpsmsdb.delivery_order d
LEFT JOIN delivery_order d
ON d.id = po.doId
AND d.deleted = 0
WHERE dop.deleted = 0
@@ -1344,11 +1344,11 @@ return MessageResponse(
GROUP_CONCAT(DISTINCT d.id ORDER BY d.id SEPARATOR ',') AS deliveryOrderIdsStr,
GROUP_CONCAT(DISTINCT d.code ORDER BY d.code SEPARATOR ',') AS deliveryNosStr,
MIN(po.consoCode) AS consoCode
FROM fpsmsdb.delivery_order_pick_order dop
INNER JOIN fpsmsdb.pick_order po
FROM delivery_order_pick_order dop
INNER JOIN pick_order po
ON po.deliveryOrderPickOrderId = dop.id
AND po.deleted = 0
LEFT JOIN fpsmsdb.delivery_order d
LEFT JOIN delivery_order d
ON d.id = po.doId
AND d.deleted = 0
WHERE dop.deleted = 0
@@ -1495,14 +1495,14 @@ return MessageResponse(
dop.requiredDeliveryDate AS requiredDeliveryDate,
dop.handlerName AS handlerName,
COALESCE(SUM(polCount.cnt), 0) AS numberOfFGItems
FROM fpsmsdb.delivery_order_pick_order dop
FROM delivery_order_pick_order dop
LEFT JOIN (
SELECT
po.deliveryOrderPickOrderId AS dopId,
po.id AS pickOrderId,
COUNT(pol.id) AS cnt
FROM fpsmsdb.pick_order po
LEFT JOIN fpsmsdb.pick_order_line pol
FROM pick_order po
LEFT JOIN pick_order_line pol
ON pol.poId = po.id
AND pol.deleted = 0
WHERE po.deleted = 0
@@ -1717,7 +1717,7 @@ return MessageResponse(
val now = LocalDateTime.now()
jdbcDao.executeUpdate(
"""
UPDATE fpsmsdb.pick_order po
UPDATE pick_order po
SET po.assignTo = NULL,
po.status = 'pending',
po.modified = :modified,
@@ -1757,8 +1757,8 @@ return MessageResponse(
jdbcDao.queryForList(
"""
SELECT dop.storeId AS storeId
FROM fpsmsdb.pick_order po
INNER JOIN fpsmsdb.delivery_order_pick_order dop
FROM pick_order po
INNER JOIN delivery_order_pick_order dop
ON dop.id = po.deliveryOrderPickOrderId AND dop.deleted = 0
WHERE po.id = :poId AND po.deleted = 0
LIMIT 1
@@ -1779,9 +1779,9 @@ return MessageResponse(
jdbcDao.queryForList(
"""
SELECT DISTINCT s.code AS supplierCode
FROM fpsmsdb.pick_order po
INNER JOIN fpsmsdb.delivery_order d ON d.id = po.doId AND d.deleted = 0
LEFT JOIN fpsmsdb.shop s ON s.id = d.supplierId AND s.deleted = 0
FROM pick_order po
INNER JOIN delivery_order d ON d.id = po.doId AND d.deleted = 0
LEFT JOIN shop s ON s.id = d.supplierId AND s.deleted = 0
WHERE po.id = :poId AND po.deleted = 0
""".trimIndent(),
mapOf("poId" to pickOrderId),
@@ -1809,9 +1809,9 @@ return MessageResponse(
jdbcDao.queryForList(
"""
SELECT DISTINCT s.code AS supplierCode
FROM fpsmsdb.pick_order po
INNER JOIN fpsmsdb.delivery_order d ON d.id = po.doId AND d.deleted = 0
LEFT JOIN fpsmsdb.shop s ON s.id = d.supplierId AND s.deleted = 0
FROM pick_order po
INNER JOIN delivery_order d ON d.id = po.doId AND d.deleted = 0
LEFT JOIN shop s ON s.id = d.supplierId AND s.deleted = 0
WHERE po.deliveryOrderPickOrderId = :dopoId AND po.deleted = 0
""".trimIndent(),
mapOf("dopoId" to dopoId),
@@ -1854,17 +1854,17 @@ return MessageResponse(
return """
AND EXISTS (
SELECT 1
FROM fpsmsdb.pick_order po
INNER JOIN fpsmsdb.delivery_order d ON d.id = po.doId AND d.deleted = 0
LEFT JOIN fpsmsdb.shop s ON s.id = d.supplierId AND s.deleted = 0
FROM pick_order po
INNER JOIN delivery_order d ON d.id = po.doId AND d.deleted = 0
LEFT JOIN shop s ON s.id = d.supplierId AND s.deleted = 0
WHERE po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0
AND ($floorExpr) = '$key'
)
AND NOT EXISTS (
SELECT 1
FROM fpsmsdb.pick_order po2
INNER JOIN fpsmsdb.delivery_order d2 ON d2.id = po2.doId AND d2.deleted = 0
LEFT JOIN fpsmsdb.shop s2 ON s2.id = d2.supplierId AND s2.deleted = 0
FROM pick_order po2
INNER JOIN delivery_order d2 ON d2.id = po2.doId AND d2.deleted = 0
LEFT JOIN shop s2 ON s2.id = d2.supplierId AND s2.deleted = 0
WHERE po2.deliveryOrderPickOrderId = dop.id AND po2.deleted = 0
AND (CASE WHEN s2.code IN ($in4) THEN '4F' ELSE '2F' END) <> '$key'
)
@@ -1903,11 +1903,11 @@ return MessageResponse(
dop.truckDepartureTime AS truckDepartureTime,
(
SELECT GROUP_CONCAT(DISTINCT d.code ORDER BY d.code SEPARATOR ',')
FROM fpsmsdb.pick_order po
INNER JOIN fpsmsdb.delivery_order d ON d.id = po.doId AND d.deleted = 0
FROM pick_order po
INNER JOIN delivery_order d ON d.id = po.doId AND d.deleted = 0
WHERE po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0
) AS doCodesStr
FROM fpsmsdb.delivery_order_pick_order dop
FROM delivery_order_pick_order dop
WHERE dop.deleted = 0
AND dop.ticketStatus IN ('pending', 'released')
AND $dateClause
@@ -2025,14 +2025,14 @@ return MessageResponse(
dop.shopCode as ShopCode,
dop.shopName as ShopName,
dop.ticketStatus as doTicketStatus
FROM fpsmsdb.delivery_order_pick_order dop
FROM delivery_order_pick_order dop
WHERE dop.handledBy = :userId

AND dop.ticketStatus IN ('pending', 'released')
AND dop.deleted = 0
AND EXISTS (
SELECT 1
FROM fpsmsdb.pick_order po
FROM pick_order po
WHERE po.deliveryOrderPickOrderId = dop.id
AND po.assignTo = :userId

@@ -2096,8 +2096,8 @@ return MessageResponse(
po.consoCode,
po.status,
DATE_FORMAT(po.targetDate, '%Y-%m-%d') as targetDate
FROM fpsmsdb.pick_order po
LEFT JOIN fpsmsdb.delivery_order do_tbl ON do_tbl.id = po.doId
FROM pick_order po
LEFT JOIN delivery_order do_tbl ON do_tbl.id = po.doId
WHERE po.deliveryOrderPickOrderId = :doPickOrderId
AND po.assignTo = :userId
AND po.type = 'do'
@@ -2154,7 +2154,7 @@ return MessageResponse(
dop.shopCode as ShopCode,
dop.shopName as ShopName,
dop.ticketStatus as doTicketStatus
FROM fpsmsdb.delivery_order_pick_order dop
FROM delivery_order_pick_order dop
WHERE dop.id = :dopoId
AND dop.deleted = 0
LIMIT 1
@@ -2172,8 +2172,8 @@ return MessageResponse(
po.consoCode,
po.status,
DATE_FORMAT(po.targetDate, '%Y-%m-%d') as targetDate
FROM fpsmsdb.pick_order po
LEFT JOIN fpsmsdb.delivery_order do_tbl ON do_tbl.id = po.doId
FROM pick_order po
LEFT JOIN delivery_order do_tbl ON do_tbl.id = po.doId
WHERE po.deliveryOrderPickOrderId = :dopoId
AND po.type = 'do'
AND po.deleted = 0
@@ -2228,24 +2228,24 @@ return MessageResponse(
GROUP_CONCAT(DISTINCT po.code ORDER BY po.id SEPARATOR ', ') as pickOrderCodes,
GROUP_CONCAT(DISTINCT do_tbl.id ORDER BY do_tbl.id) as deliveryOrderIds,
GROUP_CONCAT(DISTINCT do_tbl.code ORDER BY do_tbl.id SEPARATOR ', ') as deliveryNos,
(SELECT po2.consoCode FROM fpsmsdb.pick_order po2 WHERE po2.id = MIN(po.id) LIMIT 1) as pickOrderConsoCode,
(SELECT po2.targetDate FROM fpsmsdb.pick_order po2 WHERE po2.id = MIN(po.id) LIMIT 1) as pickOrderTargetDate,
(SELECT po2.status FROM fpsmsdb.pick_order po2 WHERE po2.id = MIN(po.id) LIMIT 1) as pickOrderStatus,
(SELECT do2.orderDate FROM fpsmsdb.delivery_order do2 WHERE do2.id = MIN(do_tbl.id) LIMIT 1) as deliveryDate,
(SELECT po2.consoCode FROM pick_order po2 WHERE po2.id = MIN(po.id) LIMIT 1) as pickOrderConsoCode,
(SELECT po2.targetDate FROM pick_order po2 WHERE po2.id = MIN(po.id) LIMIT 1) as pickOrderTargetDate,
(SELECT po2.status FROM pick_order po2 WHERE po2.id = MIN(po.id) LIMIT 1) as pickOrderStatus,
(SELECT do2.orderDate FROM delivery_order do2 WHERE do2.id = MIN(do_tbl.id) LIMIT 1) as deliveryDate,
COUNT(DISTINCT po.id) as numberOfPickOrders,
(SELECT SUM(pol_count.line_count)
FROM (
SELECT po3.id, COUNT(*) as line_count
FROM fpsmsdb.pick_order po3
JOIN fpsmsdb.pick_order_line pol3 ON pol3.poId = po3.id AND pol3.deleted = false
FROM pick_order po3
JOIN pick_order_line pol3 ON pol3.poId = po3.id AND pol3.deleted = false
WHERE po3.deliveryOrderPickOrderId = dop.id AND po3.deleted = 0
GROUP BY po3.id
) pol_count
) as numberOfCartons
FROM fpsmsdb.delivery_order_pick_order dop
INNER JOIN fpsmsdb.pick_order po ON po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0
LEFT JOIN fpsmsdb.delivery_order do_tbl ON do_tbl.id = po.doId
LEFT JOIN fpsmsdb.shop s ON s.id = dop.shopId
FROM delivery_order_pick_order dop
INNER JOIN pick_order po ON po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0
LEFT JOIN delivery_order do_tbl ON do_tbl.id = po.doId
LEFT JOIN shop s ON s.id = dop.shopId
WHERE po.assignTo = :userId
AND po.type = 'do'
AND po.status IN ('assigned', 'released', 'picking')
@@ -2728,7 +2728,7 @@ return MessageResponse(
*/
private fun tryCompleteDeliveryOrderPickOrderTicketCompleted(poId: Long) {
val dopRow = jdbcDao.queryForMap(
"SELECT deliveryOrderPickOrderId AS dopId FROM fpsmsdb.pick_order WHERE id = :poId AND deleted = 0",
"SELECT deliveryOrderPickOrderId AS dopId FROM pick_order WHERE id = :poId AND deleted = 0",
mapOf("poId" to poId),
).orElse(null) ?: return
val dopId = (dopRow["dopId"] as? Number)?.toLong() ?: return
@@ -2737,7 +2737,7 @@ return MessageResponse(
val unfinished = jdbcDao.queryForInt(
"""
SELECT COUNT(1)
FROM fpsmsdb.pick_order po
FROM pick_order po
WHERE po.deliveryOrderPickOrderId = :dopId
AND po.deleted = 0
AND po.type = 'do'
@@ -2757,7 +2757,7 @@ return MessageResponse(

jdbcDao.executeUpdate(
"""
UPDATE fpsmsdb.delivery_order_pick_order dop
UPDATE delivery_order_pick_order dop
SET dop.ticketStatus = 'completed',
dop.ticketCompleteDateTime = NOW(),
dop.deliveryNoteCode = CASE
@@ -2781,7 +2781,7 @@ return MessageResponse(
jdbcDao.queryForList(
"""
SELECT DISTINCT po.doId AS doId
FROM fpsmsdb.pick_order po
FROM pick_order po
WHERE po.deliveryOrderPickOrderId = :dopId
AND po.deleted = 0
AND po.doId IS NOT NULL
@@ -2974,7 +2974,7 @@ return MessageResponse(
val rows = jdbcDao.queryForList(
"""
SELECT po.id AS id, po.code AS code, po.doId AS doId
FROM fpsmsdb.pick_order po
FROM pick_order po
WHERE po.deliveryOrderPickOrderId = :dopoId
AND po.deleted = 0
""".trimIndent(),


+ 31
- 31
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchReleaseService.kt Переглянути файл

@@ -186,7 +186,7 @@ open class DoWorkbenchReleaseService(
val statusRows = jdbcDao.queryForList(
"""
SELECT status
FROM fpsmsdb.delivery_order
FROM delivery_order
WHERE id = :id AND deleted = 0
""".trimIndent(),
mapOf("id" to deliveryOrderId)
@@ -342,7 +342,7 @@ open class DoWorkbenchReleaseService(
val statusRows = jdbcDao.queryForList(
"""
SELECT status
FROM fpsmsdb.delivery_order
FROM delivery_order
WHERE id = :id AND deleted = 0
""".trimIndent(),
mapOf("id" to deliveryOrderId)
@@ -411,10 +411,10 @@ open class DoWorkbenchReleaseService(
val floor = storeDisplay.replace("/", "").trim()
val prefix = "TI-$ticketLetter-$ymd-$floor-"
val sql = """
SELECT ticket_no AS t FROM fpsmsdb.do_pick_order
SELECT ticket_no AS t FROM do_pick_order
WHERE ticket_no LIKE CONCAT(:prefix, '%')
UNION ALL
SELECT ticketNo AS t FROM fpsmsdb.delivery_order_pick_order
SELECT ticketNo AS t FROM delivery_order_pick_order
WHERE ticketNo LIKE CONCAT(:prefix, '%')
""".trimIndent()
val rows = try {
@@ -473,7 +473,7 @@ open class DoWorkbenchReleaseService(
return try {
val sql = """
SELECT do.id
FROM fpsmsdb.delivery_order do
FROM delivery_order do
WHERE do.deleted = 0
AND do.id IN (${ids.joinToString(",")})
ORDER BY DATE(do.estimatedArrivalDate), do.id
@@ -622,9 +622,9 @@ open class DoWorkbenchReleaseService(
"""
AND NOT EXISTS (
SELECT 1
FROM fpsmsdb.pick_order po
INNER JOIN fpsmsdb.delivery_order d ON d.id = po.doId AND d.deleted = 0
LEFT JOIN fpsmsdb.shop s ON s.id = d.supplierId AND s.deleted = 0
FROM pick_order po
INNER JOIN delivery_order d ON d.id = po.doId AND d.deleted = 0
LEFT JOIN shop s ON s.id = d.supplierId AND s.deleted = 0
WHERE po.deliveryOrderPickOrderId = delivery_order_pick_order.id
AND po.deleted = 0
AND ($floorExpr) <> '$floor'
@@ -636,14 +636,14 @@ open class DoWorkbenchReleaseService(
"""
AND (
NOT EXISTS (
SELECT 1 FROM fpsmsdb.pick_order po0
SELECT 1 FROM pick_order po0
WHERE po0.deliveryOrderPickOrderId = delivery_order_pick_order.id AND po0.deleted = 0
)
OR EXISTS (
SELECT 1
FROM fpsmsdb.pick_order po
INNER JOIN fpsmsdb.delivery_order d ON d.id = po.doId AND d.deleted = 0
LEFT JOIN fpsmsdb.shop s ON s.id = d.supplierId AND s.deleted = 0
FROM pick_order po
INNER JOIN delivery_order d ON d.id = po.doId AND d.deleted = 0
LEFT JOIN shop s ON s.id = d.supplierId AND s.deleted = 0
WHERE po.deliveryOrderPickOrderId = delivery_order_pick_order.id
AND po.deleted = 0
AND ($floorExpr) = '$floor'
@@ -684,7 +684,7 @@ open class DoWorkbenchReleaseService(
val sql = StringBuilder(
"""
SELECT id, releaseType, ticketNo
FROM fpsmsdb.delivery_order_pick_order
FROM delivery_order_pick_order
WHERE deleted = 0
AND shopId = :shopId
AND requiredDeliveryDate = :requiredDate
@@ -729,7 +729,7 @@ open class DoWorkbenchReleaseService(
val sql = StringBuilder(
"""
SELECT id, releaseType, ticketNo
FROM fpsmsdb.delivery_order_pick_order
FROM delivery_order_pick_order
WHERE deleted = 0
AND shopId = :shopId
AND requiredDeliveryDate = :requiredDate
@@ -768,7 +768,7 @@ open class DoWorkbenchReleaseService(
val sql = StringBuilder(
"""
SELECT id, releaseType, ticketNo
FROM fpsmsdb.delivery_order_pick_order
FROM delivery_order_pick_order
WHERE deleted = 0
AND shopId = :shopId
AND requiredDeliveryDate = :requiredDate
@@ -805,7 +805,7 @@ open class DoWorkbenchReleaseService(
val sql = StringBuilder(
"""
SELECT id, releaseType, ticketNo
FROM fpsmsdb.delivery_order_pick_order
FROM delivery_order_pick_order
WHERE deleted = 0
AND shopId = :shopId
AND requiredDeliveryDate = :requiredDate
@@ -841,7 +841,7 @@ open class DoWorkbenchReleaseService(
val sql = StringBuilder(
"""
SELECT id, releaseType, ticketNo
FROM fpsmsdb.delivery_order_pick_order
FROM delivery_order_pick_order
WHERE deleted = 0
AND shopId = :shopId
AND requiredDeliveryDate = :requiredDate
@@ -868,7 +868,7 @@ open class DoWorkbenchReleaseService(
val rows = jdbcDao.queryForList(
"""
SELECT handledBy
FROM fpsmsdb.delivery_order_pick_order
FROM delivery_order_pick_order
WHERE id = :id AND deleted = 0
""".trimIndent(),
mapOf("id" to ticketId),
@@ -885,7 +885,7 @@ open class DoWorkbenchReleaseService(
private fun setRelationshipIdSelf(headerId: Long, actor: String) {
jdbcDao.executeUpdate(
"""
UPDATE fpsmsdb.delivery_order_pick_order
UPDATE delivery_order_pick_order
SET relationshipId = :headerId,
modified = :modified,
modifiedBy = :modifiedBy
@@ -902,7 +902,7 @@ open class DoWorkbenchReleaseService(
private fun softDeleteMergedSourceTicket(sourceId: Long, successorId: Long) {
jdbcDao.executeUpdate(
"""
UPDATE fpsmsdb.delivery_order_pick_order
UPDATE delivery_order_pick_order
SET deleted = 1,
relationshipId = :successorId,
modified = :modified,
@@ -921,7 +921,7 @@ open class DoWorkbenchReleaseService(
private fun reparentPickOrdersFromHeader(sourceHeaderId: Long, targetHeaderId: Long) {
jdbcDao.executeUpdate(
"""
UPDATE fpsmsdb.pick_order
UPDATE pick_order
SET deliveryOrderPickOrderId = :targetHeaderId
WHERE deliveryOrderPickOrderId = :sourceHeaderId AND deleted = 0
""".trimIndent(),
@@ -970,7 +970,7 @@ open class DoWorkbenchReleaseService(
group.forEach { r ->
jdbcDao.executeUpdate(
"""
UPDATE fpsmsdb.pick_order
UPDATE pick_order
SET deliveryOrderPickOrderId = :headerId
WHERE id = :pickOrderId
""".trimIndent(),
@@ -992,7 +992,7 @@ open class DoWorkbenchReleaseService(
val now = LocalDateTime.now()
jdbcDao.executeUpdate(
"""
INSERT INTO fpsmsdb.delivery_order_pick_order (
INSERT INTO delivery_order_pick_order (
truckId, shopId, storeId, requiredDeliveryDate, truckDepartureTime,
truckLanceCode, shopCode, shopName, loadingSequence, ticketNo,
ticketReleaseTime, ticketStatus, releaseType, handledBy, handlerName,
@@ -1025,7 +1025,7 @@ open class DoWorkbenchReleaseService(
return jdbcDao.queryForList(
"""
SELECT id
FROM fpsmsdb.delivery_order_pick_order
FROM delivery_order_pick_order
WHERE ticketNo = :ticketNo
ORDER BY id DESC
LIMIT 1
@@ -1267,7 +1267,7 @@ open class DoWorkbenchReleaseService(
retireSourceTicketIntoMergeHeader(isExtraDopoId, mergeHeaderId)

val mergeTicketNo = jdbcDao.queryForList(
"SELECT ticketNo AS t FROM fpsmsdb.delivery_order_pick_order WHERE id = :id",
"SELECT ticketNo AS t FROM delivery_order_pick_order WHERE id = :id",
mapOf("id" to mergeHeaderId),
).firstOrNull()?.let { row ->
row.entries.find { it.key.equals("t", true) }?.value?.toString()
@@ -1339,11 +1339,11 @@ open class DoWorkbenchReleaseService(
dop.loadingSequence AS loadingSequence,
(
SELECT GROUP_CONCAT(DISTINCT d.code ORDER BY d.code SEPARATOR ',')
FROM fpsmsdb.pick_order po
INNER JOIN fpsmsdb.delivery_order d ON d.id = po.doId AND d.deleted = 0
FROM pick_order po
INNER JOIN delivery_order d ON d.id = po.doId AND d.deleted = 0
WHERE po.deliveryOrderPickOrderId = dop.id AND po.deleted = 0
) AS doCodesStr
FROM fpsmsdb.delivery_order_pick_order dop
FROM delivery_order_pick_order dop
WHERE dop.deleted = 0
AND dop.requiredDeliveryDate = :requiredDate
AND dop.ticketStatus IN ('pending', 'released')
@@ -1495,7 +1495,7 @@ open class DoWorkbenchReleaseService(
SELECT
id, ticketNo, releaseType, shopId, shopCode, shopName, storeId, truckId,
requiredDeliveryDate, truckLanceCode, truckDepartureTime, loadingSequence
FROM fpsmsdb.delivery_order_pick_order
FROM delivery_order_pick_order
WHERE id = :id AND deleted = 0
""".trimIndent(),
mapOf("id" to id),
@@ -1532,7 +1532,7 @@ open class DoWorkbenchReleaseService(
val now = LocalDateTime.now()
jdbcDao.executeUpdate(
"""
INSERT INTO fpsmsdb.delivery_order_pick_order (
INSERT INTO delivery_order_pick_order (
truckId, shopId, storeId, requiredDeliveryDate, truckDepartureTime,
truckLanceCode, shopCode, shopName, loadingSequence, ticketNo,
ticketReleaseTime, ticketStatus, releaseType, handledBy, handlerName,
@@ -1564,7 +1564,7 @@ open class DoWorkbenchReleaseService(
)
return jdbcDao.queryForList(
"""
SELECT id FROM fpsmsdb.delivery_order_pick_order
SELECT id FROM delivery_order_pick_order
WHERE ticketNo = :ticketNo ORDER BY id DESC LIMIT 1
""".trimIndent(),
mapOf("ticketNo" to ticketNo),


+ 70
- 70
src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt Переглянути файл

@@ -175,7 +175,7 @@ open class JoPickOrderService(
val joPickOrders = itemIds.map { itemId ->
// Get item code from database - 修复:使用 queryForList
val itemCodeResult = jdbcDao.queryForList(
"SELECT code FROM fpsmsdb.items WHERE id = :itemId",
"SELECT code FROM items WHERE id = :itemId",
mapOf("itemId" to itemId)
)
val itemCode = if (itemCodeResult.isNotEmpty()) {
@@ -207,7 +207,7 @@ open class JoPickOrderService(
val joPickOrderRecords = itemIds.map { itemId ->
// Get item code from database - 修复:使用 queryForList
val itemCodeResult = jdbcDao.queryForList(
"SELECT code FROM fpsmsdb.items WHERE id = :itemId",
"SELECT code FROM items WHERE id = :itemId",
mapOf("itemId" to itemId)
)
val itemCode = if (itemCodeResult.isNotEmpty()) {
@@ -1102,10 +1102,10 @@ open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse
i.name as itemName,
pol.qty as requiredQty,
u.name as pickerName
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id
JOIN fpsmsdb.items i ON i.id = pol.itemId
LEFT JOIN fpsmsdb.user u ON u.id = :createdBy
FROM pick_order po
JOIN pick_order_line pol ON pol.poId = po.id
JOIN items i ON i.id = pol.itemId
LEFT JOIN user u ON u.id = :createdBy
WHERE po.id = :pickOrderId
AND pol.itemId = :itemId
AND pol.deleted = false
@@ -1151,10 +1151,10 @@ open fun recordSecondScanIssue(request: SecondScanIssueRequest): MessageResponse
ill.id as lotId,
il.lotNo as lotNo,
w.name as storeLocation
FROM fpsmsdb.suggested_pick_lot spl
JOIN fpsmsdb.inventory_lot_line ill ON ill.id = COALESCE(spl.actualPickLotLineId, spl.suggestedLotLineId)
JOIN fpsmsdb.inventory_lot il ON ill.inventoryLotId = il.id
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
FROM suggested_pick_lot spl
JOIN inventory_lot_line ill ON ill.id = COALESCE(spl.actualPickLotLineId, spl.suggestedLotLineId)
JOIN inventory_lot il ON ill.inventoryLotId = il.id
LEFT JOIN warehouse w ON w.id = ill.warehouseId
WHERE spl.pickOrderLineId = :pickOrderLineId
AND spl.deleted = false
AND ill.deleted = false
@@ -1479,7 +1479,7 @@ open fun getCompletedJobOrderPickOrderLotDetails(pickOrderId: Long): List<Map<St
-- Calculate total picked quantity by ALL pick orders for this lot
COALESCE((
SELECT SUM(sol_all.qty)
FROM fpsmsdb.stock_out_line sol_all
FROM stock_out_line sol_all
WHERE sol_all.inventoryLotLineId = ill.id
AND sol_all.deleted = false
AND sol_all.status IN ('pending', 'checked', 'partially_completed', 'completed')
@@ -1510,28 +1510,28 @@ open fun getCompletedJobOrderPickOrderLotDetails(pickOrderId: Long): List<Map<St
jpo.match_by as match_by,
jpo.match_qty as match_qty
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.job_order jo ON jo.id = po.joId
LEFT JOIN fpsmsdb.bom b ON b.id = jo.bomId
LEFT JOIN fpsmsdb.items fg ON fg.id = b.itemId
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id
JOIN fpsmsdb.items i ON i.id = pol.itemId
LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId
LEFT JOIN fpsmsdb.suggested_pick_lot spl ON pol.id = spl.pickOrderLineId
LEFT JOIN fpsmsdb.inventory_lot_line ill ON ill.id = COALESCE(spl.actualPickLotLineId, spl.suggestedLotLineId)
LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
FROM pick_order po
JOIN job_order jo ON jo.id = po.joId
LEFT JOIN bom b ON b.id = jo.bomId
LEFT JOIN items fg ON fg.id = b.itemId
JOIN pick_order_line pol ON pol.poId = po.id
JOIN items i ON i.id = pol.itemId
LEFT JOIN uom_conversion uc ON uc.id = pol.uomId
LEFT JOIN suggested_pick_lot spl ON pol.id = spl.pickOrderLineId
LEFT JOIN inventory_lot_line ill ON ill.id = COALESCE(spl.actualPickLotLineId, spl.suggestedLotLineId)
LEFT JOIN inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN warehouse w ON w.id = ill.warehouseId
-- no-lot 情況:ill.id 為 null 時,要匹配 sol.inventoryLotLineId IS NULL
LEFT JOIN fpsmsdb.stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.deleted = false
LEFT JOIN stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.deleted = false
AND (
sol.inventoryLotLineId = ill.id
OR (sol.inventoryLotLineId IS NULL AND ill.id IS NULL)
)
LEFT JOIN fpsmsdb.jo_pick_order jpo ON jpo.pick_order_id = po.id AND jpo.item_id = pol.itemId
LEFT JOIN fpsmsdb.item_uom iu ON iu.itemId = b.itemId AND iu.stockUnit = 1
LEFT JOIN fpsmsdb.uom_conversion uc_fg ON uc_fg.id = iu.uomId
LEFT JOIN fpsmsdb.uom_conversion uc_bom ON uc_bom.id = iu.uomId
LEFT JOIN jo_pick_order jpo ON jpo.pick_order_id = po.id AND jpo.item_id = pol.itemId
LEFT JOIN item_uom iu ON iu.itemId = b.itemId AND iu.stockUnit = 1
LEFT JOIN uom_conversion uc_fg ON uc_fg.id = iu.uomId
LEFT JOIN uom_conversion uc_bom ON uc_bom.id = iu.uomId
WHERE po.deleted = false
AND po.id = :pickOrderId
AND pol.deleted = false
@@ -1904,7 +1904,7 @@ open fun getCompletedJobOrderPickOrders(completedDate: LocalDate?): List<Map<Str
-- Calculate total picked quantity by ALL pick orders for this lot
COALESCE((
SELECT SUM(sol_all.qty)
FROM fpsmsdb.stock_out_line sol_all
FROM stock_out_line sol_all
WHERE sol_all.inventoryLotLineId = ill.id
AND sol_all.deleted = false
AND sol_all.status IN ('pending', 'checked', 'partially_completed', 'completed', 'picking')
@@ -1936,27 +1936,27 @@ open fun getCompletedJobOrderPickOrders(completedDate: LocalDate?): List<Map<Str
jpo.match_by as match_by,
jpo.match_qty as match_qty
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.job_order jo ON jo.id = po.joId
LEFT JOIN fpsmsdb.bom b ON b.id = jo.bomId
LEFT JOIN fpsmsdb.items fg ON fg.id = b.itemId
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id
JOIN fpsmsdb.items i ON i.id = pol.itemId
LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId
LEFT JOIN fpsmsdb.suggested_pick_lot spl ON pol.id = spl.pickOrderLineId
LEFT JOIN fpsmsdb.inventory_lot_line ill ON ill.id = COALESCE(spl.actualPickLotLineId, spl.suggestedLotLineId)
LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
FROM pick_order po
JOIN job_order jo ON jo.id = po.joId
LEFT JOIN bom b ON b.id = jo.bomId
LEFT JOIN items fg ON fg.id = b.itemId
JOIN pick_order_line pol ON pol.poId = po.id
JOIN items i ON i.id = pol.itemId
LEFT JOIN uom_conversion uc ON uc.id = pol.uomId
LEFT JOIN suggested_pick_lot spl ON pol.id = spl.pickOrderLineId
LEFT JOIN inventory_lot_line ill ON ill.id = COALESCE(spl.actualPickLotLineId, spl.suggestedLotLineId)
LEFT JOIN inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN warehouse w ON w.id = ill.warehouseId
-- no-lot:ill.id 為 null 時,匹配 sol.inventoryLotLineId IS NULL
LEFT JOIN fpsmsdb.stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.deleted = false
LEFT JOIN stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.deleted = false
AND (
sol.inventoryLotLineId = ill.id
OR (sol.inventoryLotLineId IS NULL AND ill.id IS NULL)
)
LEFT JOIN fpsmsdb.jo_pick_order jpo ON jpo.pick_order_id = po.id AND jpo.item_id = pol.itemId
LEFT JOIN fpsmsdb.item_uom iu ON iu.itemId = b.itemId AND iu.stockUnit = 1
LEFT JOIN fpsmsdb.uom_conversion uc_fg ON uc_fg.id = iu.uomId
LEFT JOIN fpsmsdb.uom_conversion uc_bom ON uc_bom.id = iu.uomId
LEFT JOIN jo_pick_order jpo ON jpo.pick_order_id = po.id AND jpo.item_id = pol.itemId
LEFT JOIN item_uom iu ON iu.itemId = b.itemId AND iu.stockUnit = 1
LEFT JOIN uom_conversion uc_fg ON uc_fg.id = iu.uomId
LEFT JOIN uom_conversion uc_bom ON uc_bom.id = iu.uomId
WHERE po.deleted = false
AND po.id = :pickOrderId
AND pol.deleted = false
@@ -2063,7 +2063,7 @@ open fun getCompletedJobOrderPickOrders(completedDate: LocalDate?): List<Map<Str
-- Calculate total picked quantity by ALL pick orders for this lot
COALESCE((
SELECT SUM(sol_all.qty)
FROM fpsmsdb.stock_out_line sol_all
FROM stock_out_line sol_all
WHERE sol_all.inventoryLotLineId = ill.id
AND sol_all.deleted = false
AND sol_all.status IN ('pending', 'checked', 'partially_completed', 'completed')
@@ -2094,23 +2094,23 @@ open fun getCompletedJobOrderPickOrders(completedDate: LocalDate?): List<Map<Str
jpo.match_by as match_by,
jpo.match_qty as match_qty
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.job_order jo ON jo.id = po.joId
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id
JOIN fpsmsdb.items i ON i.id = pol.itemId
LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId
LEFT JOIN fpsmsdb.suggested_pick_lot spl ON pol.id = spl.pickOrderLineId
LEFT JOIN fpsmsdb.inventory_lot_line ill ON ill.id = COALESCE(spl.actualPickLotLineId, spl.suggestedLotLineId)
LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
FROM pick_order po
JOIN job_order jo ON jo.id = po.joId
JOIN pick_order_line pol ON pol.poId = po.id
JOIN items i ON i.id = pol.itemId
LEFT JOIN uom_conversion uc ON uc.id = pol.uomId
LEFT JOIN suggested_pick_lot spl ON pol.id = spl.pickOrderLineId
LEFT JOIN inventory_lot_line ill ON ill.id = COALESCE(spl.actualPickLotLineId, spl.suggestedLotLineId)
LEFT JOIN inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN warehouse w ON w.id = ill.warehouseId
-- no-lot 情況:ill.id 為 null 時,要匹配 sol.inventoryLotLineId IS NULL 的记录
LEFT JOIN fpsmsdb.stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.deleted = false
LEFT JOIN stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.deleted = false
AND (
sol.inventoryLotLineId = ill.id
OR (sol.inventoryLotLineId IS NULL AND ill.id IS NULL)
)
LEFT JOIN fpsmsdb.jo_pick_order jpo ON jpo.pick_order_id = po.id AND jpo.item_id = pol.itemId
LEFT JOIN jo_pick_order jpo ON jpo.pick_order_id = po.id AND jpo.item_id = pol.itemId

WHERE po.deleted = false
AND po.id = :pickOrderId
@@ -2196,12 +2196,12 @@ open fun getCompletedJobOrderPickOrders(completedDate: LocalDate?): List<Map<Str
WHEN LOWER(sol.status) = 'completed' THEN pol.id
WHEN pol.status = 'COMPLETED' THEN pol.id
END) AS finishedCount
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id AND pol.deleted = false
LEFT JOIN fpsmsdb.suggested_pick_lot spl ON pol.id = spl.pickOrderLineId AND spl.deleted = false
LEFT JOIN fpsmsdb.inventory_lot_line ill ON ill.id = COALESCE(spl.actualPickLotLineId, spl.suggestedLotLineId) AND ill.deleted = false
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
LEFT JOIN fpsmsdb.stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.deleted = false
FROM pick_order po
JOIN pick_order_line pol ON pol.poId = po.id AND pol.deleted = false
LEFT JOIN suggested_pick_lot spl ON pol.id = spl.pickOrderLineId AND spl.deleted = false
LEFT JOIN inventory_lot_line ill ON ill.id = COALESCE(spl.actualPickLotLineId, spl.suggestedLotLineId) AND ill.deleted = false
LEFT JOIN warehouse w ON w.id = ill.warehouseId
LEFT JOIN stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.deleted = false
WHERE po.deleted = false
AND po.status = 'RELEASED'
AND po.joId IS NOT NULL
@@ -2227,9 +2227,9 @@ open fun getCompletedJobOrderPickOrders(completedDate: LocalDate?): List<Map<Str
WHEN LOWER(sol.status) = 'completed' THEN pol.id
WHEN pol.status = 'COMPLETED' THEN pol.id
END) AS finishedCount
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id AND pol.deleted = false
JOIN fpsmsdb.stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.deleted = false
FROM pick_order po
JOIN pick_order_line pol ON pol.poId = po.id AND pol.deleted = false
JOIN stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.deleted = false
WHERE po.deleted = false
AND po.status = 'RELEASED'
AND po.joId IS NOT NULL
@@ -2249,9 +2249,9 @@ open fun getCompletedJobOrderPickOrders(completedDate: LocalDate?): List<Map<Str
SELECT
po.id AS pickOrderId,
COUNT(DISTINCT sol.id) AS rejectedCount
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id AND pol.deleted = false
JOIN fpsmsdb.stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.deleted = false
FROM pick_order po
JOIN pick_order_line pol ON pol.poId = po.id AND pol.deleted = false
JOIN stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.deleted = false
WHERE po.deleted = false
AND po.status = 'RELEASED'
AND po.joId IS NOT NULL


+ 1
- 1
src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderLineRepository.kt Переглянути файл

@@ -36,7 +36,7 @@ fun findAllByPickOrderIdInAndDeletedFalse(@Param("pickOrderIds") pickOrderIds: L
@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query(
value = """
UPDATE fpsmsdb.pick_order_line
UPDATE pick_order_line
SET status = 'completed',
modified = CURRENT_TIMESTAMP
WHERE id = :pickOrderLineId


+ 3
- 3
src/main/java/com/ffii/fpsms/modules/pickOrder/entity/PickOrderRepository.kt Переглянути файл

@@ -163,7 +163,7 @@ fun findCompletedWithPlasticBoxCartonQtyInPlanStartRange(
@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query(
value = """
UPDATE fpsmsdb.pick_order
UPDATE pick_order
SET assignTo = :userId,
status = :status,
modified = :modified,
@@ -186,7 +186,7 @@ fun bulkAssignWorkbenchTicket(
@Query(
value = """
SELECT id
FROM fpsmsdb.pick_order
FROM pick_order
WHERE deliveryOrderPickOrderId = :dopoId
AND deleted = 0
""",
@@ -247,7 +247,7 @@ fun findAllReleasedJoWorkbenchPickOrders(
@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query(
value = """
UPDATE fpsmsdb.pick_order
UPDATE pick_order
SET submittedLines = COALESCE(submittedLines, 0) + 1,
modified = CURRENT_TIMESTAMP
WHERE id = :pickOrderId


+ 14
- 14
src/main/java/com/ffii/fpsms/modules/pickOrder/service/HierarchicalFgPayloadAssembler.kt Переглянути файл

@@ -54,8 +54,8 @@ fun assembleHierarchicalFgPayload(
val linesSql = """
WITH target_pol AS (
SELECT pol.id
FROM fpsmsdb.pick_order_line pol
JOIN fpsmsdb.pick_order po ON po.id = pol.poId
FROM pick_order_line pol
JOIN pick_order po ON po.id = pol.poId
WHERE po.id IN ($inClause)
AND po.deleted = false
AND pol.deleted = false
@@ -63,14 +63,14 @@ fun assembleHierarchicalFgPayload(
ll AS (
SELECT spl.pickOrderLineId,
COALESCE(spl.actualPickLotLineId, spl.suggestedLotLineId) AS lotLineId
FROM fpsmsdb.suggested_pick_lot spl
FROM suggested_pick_lot spl
JOIN target_pol tp ON tp.id = spl.pickOrderLineId
WHERE spl.deleted = false

UNION

SELECT sol.pickOrderLineId, sol.inventoryLotLineId AS lotLineId
FROM fpsmsdb.stock_out_line sol
FROM stock_out_line sol
JOIN target_pol tp ON tp.id = sol.pickOrderLineId
WHERE sol.deleted = false
),
@@ -78,7 +78,7 @@ sm AS (
SELECT s.pickOrderLineId,
COALESCE(s.actualPickLotLineId, s.suggestedLotLineId) AS displayLotLineId,
MAX(s.id) AS maxId
FROM fpsmsdb.suggested_pick_lot s
FROM suggested_pick_lot s
JOIN target_pol tp ON tp.id = s.pickOrderLineId
WHERE s.deleted = false
GROUP BY s.pickOrderLineId, COALESCE(s.actualPickLotLineId, s.suggestedLotLineId)
@@ -145,29 +145,29 @@ SELECT
WHEN sol.status = 'created' THEN 'pending'
ELSE 'pending'
END as processingStatus
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id AND pol.deleted = false
JOIN fpsmsdb.items i ON i.id = pol.itemId
LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId
FROM pick_order po
JOIN pick_order_line pol ON pol.poId = po.id AND pol.deleted = false
JOIN items i ON i.id = pol.itemId
LEFT JOIN uom_conversion uc ON uc.id = pol.uomId

LEFT JOIN ll ON ll.pickOrderLineId = pol.id

LEFT JOIN sm
ON sm.pickOrderLineId = pol.id
AND sm.displayLotLineId <=> ll.lotLineId
LEFT JOIN fpsmsdb.suggested_pick_lot spl
LEFT JOIN suggested_pick_lot spl
ON spl.id = sm.maxId
AND spl.deleted = false

LEFT JOIN fpsmsdb.stock_out_line sol
LEFT JOIN stock_out_line sol
ON sol.pickOrderLineId = pol.id
AND ((sol.inventoryLotLineId = ll.lotLineId)
OR (sol.inventoryLotLineId IS NULL AND ll.lotLineId IS NULL))
AND sol.deleted = false

LEFT JOIN fpsmsdb.inventory_lot_line ill ON ill.id = ll.lotLineId AND ill.deleted = false
LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId AND il.deleted = false
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
LEFT JOIN inventory_lot_line ill ON ill.id = ll.lotLineId AND ill.deleted = false
LEFT JOIN inventory_lot il ON il.id = ill.inventoryLotId AND il.deleted = false
LEFT JOIN warehouse w ON w.id = ill.warehouseId
WHERE po.id IN ($inClause)
AND po.deleted = false
ORDER BY


+ 3
- 3
src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickExecutionIssueService.kt Переглянути файл

@@ -1619,7 +1619,7 @@ open fun createBatchReleaseIssue(
// 查询 delivery order
val deliveryOrderSql = """
SELECT do.id, do.code, do.estimatedArrivalDate
FROM fpsmsdb.delivery_order do
FROM delivery_order do
WHERE do.id = :deliveryOrderId
AND do.deleted = 0
""".trimIndent()
@@ -1637,8 +1637,8 @@ open fun createBatchReleaseIssue(
// 查询 delivery order lines
val linesSql = """
SELECT dol.id, dol.itemId, i.code as itemCode, i.name as itemName, dol.qty
FROM fpsmsdb.delivery_order_line dol
INNER JOIN fpsmsdb.items i ON i.id = dol.itemId
FROM delivery_order_line dol
INNER JOIN items i ON i.id = dol.itemId
WHERE dol.deliveryOrderId = :deliveryOrderId
AND dol.deleted = 0
""".trimIndent()


+ 79
- 79
src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt Переглянути файл

@@ -1928,8 +1928,8 @@ open fun checkAndCompletePickOrderByConsoCode(consoCode: String): MessageRespons
private fun getPickOrderIdFromPickOrderLineId(pickOrderLineId: Long): Long? {
val sql = """
SELECT po.id as pickOrderId
FROM fpsmsdb.pick_order_line pol
JOIN fpsmsdb.pick_order po ON po.id = pol.poId
FROM pick_order_line pol
JOIN pick_order po ON po.id = pol.poId
WHERE pol.id = :pickOrderLineId
""".trimIndent()

@@ -1963,10 +1963,10 @@ open fun checkAndCompletePickOrderByConsoCode(consoCode: String): MessageRespons
-- ELSE (COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0))
-- END as convertedAvailableQty
COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0) as convertedAvailableQty
FROM fpsmsdb.suggested_pick_lot spl
JOIN fpsmsdb.inventory_lot_line ill ON ill.id = spl.suggestedLotLineId
JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN fpsmsdb.item_uom sales_iu ON sales_iu.itemId = il.itemId AND sales_iu.salesUnit = true AND sales_iu.deleted = false
FROM suggested_pick_lot spl
JOIN inventory_lot_line ill ON ill.id = spl.suggestedLotLineId
JOIN inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN item_uom sales_iu ON sales_iu.itemId = il.itemId AND sales_iu.salesUnit = true AND sales_iu.deleted = false
WHERE spl.pickOrderLineId = :pickOrderLineId
""".trimIndent()

@@ -2009,13 +2009,13 @@ open fun checkAndCompletePickOrderByConsoCode(consoCode: String): MessageRespons
COUNT(CASE WHEN sol.status IN ('rejected', 'lot-change', 'determine1') THEN 1 END) as problematicStockOutLines,
-- 检查单位不一致问题
COUNT(CASE WHEN spl.qty > pol.qty * 100 THEN 1 END) as unitMismatchLots
FROM fpsmsdb.suggested_pick_lot spl
JOIN fpsmsdb.inventory_lot_line ill ON ill.id = spl.suggestedLotLineId
JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId
JOIN fpsmsdb.pick_order_line pol ON pol.id = spl.pickOrderLineId
FROM suggested_pick_lot spl
JOIN inventory_lot_line ill ON ill.id = spl.suggestedLotLineId
JOIN inventory_lot il ON il.id = ill.inventoryLotId
JOIN pick_order_line pol ON pol.id = spl.pickOrderLineId
-- 添加销售单位转换
LEFT JOIN fpsmsdb.item_uom sales_iu ON sales_iu.itemId = il.itemId AND sales_iu.salesUnit = true AND sales_iu.deleted = false
LEFT JOIN fpsmsdb.stock_out_line sol ON sol.inventoryLotLineId = ill.id
LEFT JOIN item_uom sales_iu ON sales_iu.itemId = il.itemId AND sales_iu.salesUnit = true AND sales_iu.deleted = false
LEFT JOIN stock_out_line sol ON sol.inventoryLotLineId = ill.id
AND sol.pickOrderLineId = spl.pickOrderLineId
WHERE spl.pickOrderLineId = :pickOrderLineId
""".trimIndent()
@@ -2336,7 +2336,7 @@ open fun checkAndCompletePickOrderByConsoCode(consoCode: String): MessageRespons
-- Calculate total picked quantity from stock_out_line table
COALESCE((
SELECT SUM(sol_picked.qty)
FROM fpsmsdb.stock_out_line sol_picked
FROM stock_out_line sol_picked
WHERE sol_picked.pickOrderLineId = pol.id
AND sol_picked.deleted = false
AND sol_picked.status IN ('completed', 'COMPLETE', 'partially_completed','rejected')
@@ -2349,8 +2349,8 @@ open fun checkAndCompletePickOrderByConsoCode(consoCode: String): MessageRespons
- COALESCE(inv.onHoldQty, 0)
- COALESCE(inv.unavailableQty, 0)
)
FROM fpsmsdb.inventory inv
JOIN fpsmsdb.item_uom iu
FROM inventory inv
JOIN item_uom iu
ON iu.itemId = inv.itemId
AND iu.uomId = inv.uomId
AND iu.baseUnit = 1
@@ -2363,24 +2363,24 @@ open fun checkAndCompletePickOrderByConsoCode(consoCode: String): MessageRespons
CASE
WHEN EXISTS (
SELECT 1
FROM fpsmsdb.stock_out_line sol_check
FROM stock_out_line sol_check
WHERE sol_check.pickOrderLineId = pol.id
AND sol_check.deleted = false
AND sol_check.status NOT IN ('completed', 'COMPLETE')
) THEN false
WHEN EXISTS (
SELECT 1
FROM fpsmsdb.stock_out_line sol_check
FROM stock_out_line sol_check
WHERE sol_check.pickOrderLineId = pol.id
AND sol_check.deleted = false
) THEN true
ELSE false
END as allLotsCompleted
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id
JOIN fpsmsdb.items i ON i.id = pol.itemId
LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId
FROM pick_order po
JOIN pick_order_line pol ON pol.poId = po.id
JOIN items i ON i.id = pol.itemId
LEFT JOIN uom_conversion uc ON uc.id = pol.uomId
WHERE po.deleted = false
AND po.id IN ($pickOrderIdsStr)
AND pol.deleted = false
@@ -2390,13 +2390,13 @@ open fun checkAndCompletePickOrderByConsoCode(consoCode: String): MessageRespons
AND (
NOT EXISTS (
SELECT 1
FROM fpsmsdb.stock_out_line sol_all_complete
FROM stock_out_line sol_all_complete
WHERE sol_all_complete.pickOrderLineId = pol.id
AND sol_all_complete.deleted = false
)
OR EXISTS (
SELECT 1
FROM fpsmsdb.stock_out_line sol_incomplete
FROM stock_out_line sol_incomplete
WHERE sol_incomplete.pickOrderLineId = pol.id
AND sol_incomplete.deleted = false
AND sol_incomplete.status NOT IN ('completed', 'COMPLETE')
@@ -2855,7 +2855,7 @@ open fun checkAndCompletePickOrderByConsoCode(consoCode: String): MessageRespons
-- Calculate total picked quantity by ALL pick orders for this lot
COALESCE((
SELECT SUM(sol_all.qty)
FROM fpsmsdb.stock_out_line sol_all
FROM stock_out_line sol_all
WHERE sol_all.inventoryLotLineId = ill.id
AND sol_all.deleted = false
AND sol_all.status IN ('pending', 'checked', 'partially_completed', 'completed')
@@ -2893,15 +2893,15 @@ open fun checkAndCompletePickOrderByConsoCode(consoCode: String): MessageRespons
ELSE 'pending'
END as processingStatus
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id
JOIN fpsmsdb.items i ON i.id = pol.itemId
LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId
LEFT JOIN fpsmsdb.suggested_pick_lot spl ON pol.id = spl.pickOrderLineId
LEFT JOIN fpsmsdb.inventory_lot_line ill ON spl.suggestedLotLineId = ill.id
LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
LEFT JOIN fpsmsdb.stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.inventoryLotLineId = ill.id AND sol.deleted = false
FROM pick_order po
JOIN pick_order_line pol ON pol.poId = po.id
JOIN items i ON i.id = pol.itemId
LEFT JOIN uom_conversion uc ON uc.id = pol.uomId
LEFT JOIN suggested_pick_lot spl ON pol.id = spl.pickOrderLineId
LEFT JOIN inventory_lot_line ill ON spl.suggestedLotLineId = ill.id
LEFT JOIN inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN warehouse w ON w.id = ill.warehouseId
LEFT JOIN stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.inventoryLotLineId = ill.id AND sol.deleted = false
WHERE po.deleted = false
AND po.id IN ($pickOrderIdsStr)
AND pol.deleted = false
@@ -3494,7 +3494,7 @@ open fun checkAndCompletePickOrderByConsoCode(consoCode: String): MessageRespons
-- Calculate total picked quantity by ALL pick orders for this lot
COALESCE((
SELECT SUM(sol_all.qty)
FROM fpsmsdb.stock_out_line sol_all
FROM stock_out_line sol_all
WHERE sol_all.inventoryLotLineId = ill.id
AND sol_all.deleted = false
AND sol_all.status IN ('pending', 'checked', 'partially_completed', 'completed')
@@ -3532,15 +3532,15 @@ open fun checkAndCompletePickOrderByConsoCode(consoCode: String): MessageRespons
ELSE 'pending'
END as processingStatus
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id
JOIN fpsmsdb.items i ON i.id = pol.itemId
LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId
LEFT JOIN fpsmsdb.suggested_pick_lot spl ON pol.id = spl.pickOrderLineId
LEFT JOIN fpsmsdb.inventory_lot_line ill ON spl.suggestedLotLineId = ill.id
LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
LEFT JOIN fpsmsdb.stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.inventoryLotLineId = ill.id AND sol.deleted = false
FROM pick_order po
JOIN pick_order_line pol ON pol.poId = po.id
JOIN items i ON i.id = pol.itemId
LEFT JOIN uom_conversion uc ON uc.id = pol.uomId
LEFT JOIN suggested_pick_lot spl ON pol.id = spl.pickOrderLineId
LEFT JOIN inventory_lot_line ill ON spl.suggestedLotLineId = ill.id
LEFT JOIN inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN warehouse w ON w.id = ill.warehouseId
LEFT JOIN stock_out_line sol ON sol.pickOrderLineId = pol.id AND sol.inventoryLotLineId = ill.id AND sol.deleted = false
WHERE po.deleted = false
AND po.id IN ($pickOrderIdsStr)
AND pol.deleted = false
@@ -3662,15 +3662,15 @@ open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map<String, A
dpo.ShopCode,
dpo.ShopName,
dpo.ticket_status as doTicketStatus
FROM fpsmsdb.do_pick_order dpo
INNER JOIN fpsmsdb.do_pick_order_line dpol ON dpol.do_pick_order_id = dpo.id AND dpol.deleted = 0
INNER JOIN fpsmsdb.pick_order po ON po.id = dpol.pick_order_id
FROM do_pick_order dpo
INNER JOIN do_pick_order_line dpol ON dpol.do_pick_order_id = dpo.id AND dpol.deleted = 0
INNER JOIN pick_order po ON po.id = dpol.pick_order_id
WHERE po.assignTo = :userId
AND po.type = 'do'
AND EXISTS (
SELECT 1
FROM fpsmsdb.do_pick_order_line dpol2
INNER JOIN fpsmsdb.pick_order po2 ON po2.id = dpol2.pick_order_id
FROM do_pick_order_line dpol2
INNER JOIN pick_order po2 ON po2.id = dpol2.pick_order_id
WHERE dpol2.do_pick_order_id = dpo.id
AND dpol2.deleted = 0
AND po2.status IN ('assigned', 'released', 'picking')
@@ -3705,8 +3705,8 @@ open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map<String, A
po.consoCode,
po.status,
DATE_FORMAT(po.targetDate, '%Y-%m-%d') as targetDate
FROM fpsmsdb.do_pick_order_line dpol
INNER JOIN fpsmsdb.pick_order po ON po.id = dpol.pick_order_id
FROM do_pick_order_line dpol
INNER JOIN pick_order po ON po.id = dpol.pick_order_id
WHERE dpol.do_pick_order_id = :doPickOrderId
AND dpol.deleted = 0
AND po.deleted = false
@@ -3798,37 +3798,37 @@ open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map<String, A
WHEN sol.status = 'created' THEN 'pending'
ELSE 'pending'
END as processingStatus
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id AND pol.deleted = false
JOIN fpsmsdb.items i ON i.id = pol.itemId
LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId
FROM pick_order po
JOIN pick_order_line pol ON pol.poId = po.id AND pol.deleted = false
JOIN items i ON i.id = pol.itemId
LEFT JOIN uom_conversion uc ON uc.id = pol.uomId

LEFT JOIN (
SELECT spl.pickOrderLineId,
COALESCE(spl.actualPickLotLineId, spl.suggestedLotLineId) AS lotLineId
FROM fpsmsdb.suggested_pick_lot spl
FROM suggested_pick_lot spl
WHERE spl.deleted = false
UNION
SELECT sol.pickOrderLineId, sol.inventoryLotLineId
FROM fpsmsdb.stock_out_line sol
FROM stock_out_line sol
WHERE sol.deleted = false
) ll ON ll.pickOrderLineId = pol.id

LEFT JOIN fpsmsdb.suggested_pick_lot spl
LEFT JOIN suggested_pick_lot spl
ON spl.pickOrderLineId = pol.id
AND spl.deleted = false
AND COALESCE(spl.actualPickLotLineId, spl.suggestedLotLineId) <=> ll.lotLineId

-- lots 用(保留原逻辑)
LEFT JOIN fpsmsdb.stock_out_line sol
LEFT JOIN stock_out_line sol
ON sol.pickOrderLineId = pol.id
AND ( (sol.inventoryLotLineId = ll.lotLineId)
OR (sol.inventoryLotLineId IS NULL AND ll.lotLineId IS NULL) )
AND sol.deleted = false

LEFT JOIN fpsmsdb.inventory_lot_line ill ON ill.id = ll.lotLineId AND ill.deleted = false
LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId AND il.deleted = false
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
LEFT JOIN inventory_lot_line ill ON ill.id = ll.lotLineId AND ill.deleted = false
LEFT JOIN inventory_lot il ON il.id = ill.inventoryLotId AND il.deleted = false
LEFT JOIN warehouse w ON w.id = ill.warehouseId

WHERE po.id = :pickOrderId
AND po.deleted = false
@@ -4040,15 +4040,15 @@ open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map<String, A
dpo.ShopCode,
dpo.ShopName,
dpo.ticket_status as doTicketStatus
FROM fpsmsdb.do_pick_order dpo
INNER JOIN fpsmsdb.do_pick_order_line dpol ON dpol.do_pick_order_id = dpo.id AND dpol.deleted = 0
INNER JOIN fpsmsdb.pick_order po ON po.id = dpol.pick_order_id
FROM do_pick_order dpo
INNER JOIN do_pick_order_line dpol ON dpol.do_pick_order_id = dpo.id AND dpol.deleted = 0
INNER JOIN pick_order po ON po.id = dpol.pick_order_id
WHERE po.assignTo = :userId
AND po.type = 'do'
AND EXISTS (
SELECT 1
FROM fpsmsdb.do_pick_order_line dpol2
INNER JOIN fpsmsdb.pick_order po2 ON po2.id = dpol2.pick_order_id
FROM do_pick_order_line dpol2
INNER JOIN pick_order po2 ON po2.id = dpol2.pick_order_id
WHERE dpol2.do_pick_order_id = dpo.id
AND dpol2.deleted = 0
AND po2.status IN ('assigned', 'released', 'picking')
@@ -4075,8 +4075,8 @@ open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map<String, A
po.consoCode,
po.status,
DATE_FORMAT(po.targetDate, '%Y-%m-%d') as targetDate
FROM fpsmsdb.do_pick_order_line dpol
INNER JOIN fpsmsdb.pick_order po ON po.id = dpol.pick_order_id
FROM do_pick_order_line dpol
INNER JOIN pick_order po ON po.id = dpol.pick_order_id
WHERE dpol.do_pick_order_id = :doPickOrderId
AND dpol.deleted = 0
AND po.deleted = false
@@ -4162,32 +4162,32 @@ open fun getAllPickOrderLotsWithDetailsHierarchical(userId: Long): Map<String, A
WHEN sol.status = 'created' THEN 'pending'
ELSE 'pending'
END as processingStatus
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id AND pol.deleted = false
JOIN fpsmsdb.items i ON i.id = pol.itemId
LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId
FROM pick_order po
JOIN pick_order_line pol ON pol.poId = po.id AND pol.deleted = false
JOIN items i ON i.id = pol.itemId
LEFT JOIN uom_conversion uc ON uc.id = pol.uomId
LEFT JOIN (
SELECT spl.pickOrderLineId,
COALESCE(spl.actualPickLotLineId, spl.suggestedLotLineId) AS lotLineId
FROM fpsmsdb.suggested_pick_lot spl
FROM suggested_pick_lot spl
WHERE spl.deleted = false
UNION
SELECT sol.pickOrderLineId, sol.inventoryLotLineId
FROM fpsmsdb.stock_out_line sol
FROM stock_out_line sol
WHERE sol.deleted = false
) ll ON ll.pickOrderLineId = pol.id
LEFT JOIN fpsmsdb.suggested_pick_lot spl
LEFT JOIN suggested_pick_lot spl
ON spl.pickOrderLineId = pol.id
AND spl.deleted = false
AND COALESCE(spl.actualPickLotLineId, spl.suggestedLotLineId) <=> ll.lotLineId
LEFT JOIN fpsmsdb.stock_out_line sol
LEFT JOIN stock_out_line sol
ON sol.pickOrderLineId = pol.id
AND ( (sol.inventoryLotLineId = ll.lotLineId)
OR (sol.inventoryLotLineId IS NULL AND ll.lotLineId IS NULL) )
AND sol.deleted = false
LEFT JOIN fpsmsdb.inventory_lot_line ill ON ill.id = ll.lotLineId AND ill.deleted = false
LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId AND il.deleted = false
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
LEFT JOIN inventory_lot_line ill ON ill.id = ll.lotLineId AND ill.deleted = false
LEFT JOIN inventory_lot il ON il.id = ill.inventoryLotId AND il.deleted = false
LEFT JOIN warehouse w ON w.id = ill.warehouseId
WHERE po.id IN (:pickOrderIds)
AND po.deleted = false
ORDER BY


+ 13
- 13
src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderWorkbenchService.kt Переглянути файл

@@ -184,16 +184,16 @@ open class PickOrderWorkbenchService(
ill.status AS lotStatus,
spl.suggestedPickLotId AS suggestedPickLotId,
COALESCE(spl.splQty, sol.qty, 0) AS suggestedQty
FROM fpsmsdb.stock_out_line sol
LEFT JOIN fpsmsdb.inventory_lot_line ill ON ill.id = sol.inventoryLotLineId
LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
FROM stock_out_line sol
LEFT JOIN inventory_lot_line ill ON ill.id = sol.inventoryLotLineId
LEFT JOIN inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN warehouse w ON w.id = ill.warehouseId
LEFT JOIN (
SELECT
s.stockOutLineId AS solId,
MAX(s.id) AS suggestedPickLotId,
SUM(COALESCE(s.qty, 0)) AS splQty
FROM fpsmsdb.suggested_pick_lot s
FROM suggested_pick_lot s
WHERE s.pickOrderLineId = :pickOrderLineId
AND s.deleted = false
AND s.stockOutLineId IS NOT NULL
@@ -303,15 +303,15 @@ open class PickOrderWorkbenchService(
sol.stockOutLineId AS stockOutLineId,
sol.stockOutLineStatus AS stockOutLineStatus,
COALESCE(sol.stockOutLineQty,0) AS stockOutLineQty
FROM fpsmsdb.inventory_lot_line ill
LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
FROM inventory_lot_line ill
LEFT JOIN inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN warehouse w ON w.id = ill.warehouseId
LEFT JOIN (
SELECT
COALESCE(s.actualPickLotLineId, s.suggestedLotLineId) AS lotLineId,
MAX(s.id) AS suggestedPickLotId,
SUM(COALESCE(s.qty,0)) AS requiredQty
FROM fpsmsdb.suggested_pick_lot s
FROM suggested_pick_lot s
WHERE s.pickOrderLineId = :pickOrderLineId
AND s.deleted = false
AND COALESCE(s.actualPickLotLineId, s.suggestedLotLineId) IS NOT NULL
@@ -323,7 +323,7 @@ open class PickOrderWorkbenchService(
MAX(so.id) AS stockOutLineId,
SUBSTRING_INDEX(GROUP_CONCAT(so.status ORDER BY so.id DESC), ',', 1) AS stockOutLineStatus,
SUM(COALESCE(so.qty,0)) AS stockOutLineQty
FROM fpsmsdb.stock_out_line so
FROM stock_out_line so
WHERE so.pickOrderLineId = :pickOrderLineId
AND so.deleted = false
AND so.inventoryLotLineId IS NOT NULL
@@ -331,13 +331,13 @@ open class PickOrderWorkbenchService(
) sol ON sol.lotLineId = ill.id
WHERE ill.id IN (
SELECT DISTINCT COALESCE(s.actualPickLotLineId, s.suggestedLotLineId)
FROM fpsmsdb.suggested_pick_lot s
FROM suggested_pick_lot s
WHERE s.pickOrderLineId = :pickOrderLineId
AND s.deleted = false
AND COALESCE(s.actualPickLotLineId, s.suggestedLotLineId) IS NOT NULL
UNION
SELECT DISTINCT so.inventoryLotLineId
FROM fpsmsdb.stock_out_line so
FROM stock_out_line so
WHERE so.pickOrderLineId = :pickOrderLineId
AND so.deleted = false
AND so.inventoryLotLineId IS NOT NULL
@@ -389,7 +389,7 @@ open class PickOrderWorkbenchService(
so.id AS stockOutLineId,
so.status AS stockOutLineStatus,
COALESCE(so.qty,0) AS stockOutLineQty
FROM fpsmsdb.stock_out_line so
FROM stock_out_line so
WHERE so.pickOrderLineId = :pickOrderLineId
AND so.deleted = false
AND so.inventoryLotLineId IS NULL


+ 3
- 1
src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/PurchaseOrderController.kt Переглянути файл

@@ -26,6 +26,7 @@ import org.springframework.web.bind.annotation.*
import java.io.File
import com.ffii.fpsms.modules.purchaseOrder.web.model.PurchaseOrderSummary
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value

@RestController
@RequestMapping("/po")
@@ -33,6 +34,7 @@ class PurchaseOrderController(
private val purchaseOrderService: PurchaseOrderService,
private val stockInLineService: StockInLineService,
private val m18PurchaseOrderService: M18PurchaseOrderService,
@Value("\${fpsms.demo-mode:false}") private val demoMode: Boolean,
) {
private val logger = LoggerFactory.getLogger(PurchaseOrderController::class.java)
@GetMapping("/list")
@@ -59,7 +61,7 @@ class PurchaseOrderController(
val pageNum = request.getParameter("pageNum")?.toIntOrNull()?.coerceAtLeast(1) ?: 1
val rawCode = request.getParameter("code")?.trim()
val refreshFromM18 = request.getParameter("refreshFromM18")?.equals("true", ignoreCase = true) == true
if (refreshFromM18 && M18PurchaseOrderService.isStockInLookupPoCode(rawCode)) {
if (!demoMode && refreshFromM18 && M18PurchaseOrderService.isStockInLookupPoCode(rawCode)) {
try {
logger.info("Stock-in /po/list: M18 refresh for code={}", rawCode)
val outcome = m18PurchaseOrderService.refreshForStockInLookup(rawCode!!)


+ 2
- 2
src/main/java/com/ffii/fpsms/modules/stock/entity/StockInLineRepository.kt Переглянути файл

@@ -173,12 +173,12 @@ fun findLatestLotNoByPrefix(@Param("prefix") prefix: String): String?
@Query(
"""
SELECT sil.jobOrderId AS jobOrderId, sil.lotNo AS lotNo
FROM fpsmsdb.stock_in_line sil
FROM stock_in_line sil
WHERE sil.deleted = 0
AND sil.jobOrderId IN (:jobOrderIds)
AND sil.id IN (
SELECT MIN(s2.id)
FROM fpsmsdb.stock_in_line s2
FROM stock_in_line s2
WHERE s2.deleted = 0
AND s2.jobOrderId IN (:jobOrderIds)
GROUP BY s2.jobOrderId


+ 23
- 0
src/main/resources/application-db-demo.yml Переглянути файл

@@ -0,0 +1,23 @@
spring:
datasource:
jdbc-url: jdbc:mysql://127.0.0.1:3306/fpsms_demo?useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8
# Login below is granted on fpsms_demo only. It must not be root and must not have any grant on fpsmsdb.
username: fpsms_demo
password: FpsmsDemoOnly2026

# Demo sandbox only. Do not point this profile at fpsmsdb.
fpsms:
demo-mode: true
monitoring:
enabled: false

scheduler:
m18Sync:
enabled: false
m18Grn:
createEnabled: false
sync-alert:
enabled: false
email:
enabled: false
to-addresses: ""

+ 484
- 0
src/main/resources/demo/demo-coverage.sql Переглянути файл

@@ -0,0 +1,484 @@
-- Fictional coverage data for fpsms_demo. Safe to run again.

SET @user := (SELECT id FROM user WHERE username = 'demo' LIMIT 1);
SET @kg := (SELECT id FROM uom_conversion WHERE code = 'KG' LIMIT 1);
SET @pcs := (SELECT id FROM uom_conversion WHERE code = 'PCS' LIMIT 1);
SET @cur := (SELECT id FROM currency WHERE code = 'HKD' LIMIT 1);
SET @sup := (SELECT id FROM shop WHERE code = 'DEMO-SUP' LIMIT 1);
SET @shp := (SELECT id FROM shop WHERE code = 'DEMO-SHP' LIMIT 1);
SET @log := (SELECT id FROM m18_data_log WHERE refType = 'DEMO' LIMIT 1);
SET @whA := (SELECT id FROM warehouse WHERE code = 'DEMO-A' LIMIT 1);
SET @whB := (SELECT id FROM warehouse WHERE code = 'DEMO-B' LIMIT 1);
SET @flour := (SELECT id FROM items WHERE code = 'DEMO-RM-01' LIMIT 1);
SET @milk := (SELECT id FROM items WHERE code = 'DEMO-RM-02' LIMIT 1);
SET @loaf := (SELECT id FROM items WHERE code = 'DEMO-FG-01' LIMIT 1);
SET @bun := (SELECT id FROM items WHERE code = 'DEMO-FG-02' LIMIT 1);
SET @po3 := (SELECT id FROM purchase_order WHERE code = 'DEMO-PO-003' LIMIT 1);
SET @do3 := (SELECT id FROM delivery_order WHERE code = 'DEMO-DO-003' LIMIT 1);
SET @pkDo3 := (SELECT id FROM pick_order WHERE code = 'DEMO-PK-DO3' LIMIT 1);
SET @pkJo2 := (SELECT id FROM pick_order WHERE code = 'DEMO-PK-JO2' LIMIT 1);
SET @bom := (SELECT id FROM bom WHERE code = 'DEMO-BOM-01' LIMIT 1);

UPDATE warehouse
SET store_id = '2F', warehouse = 'W201', area = '#A', slot = '01', stockTakeSection = 'A', stockTakeSectionDescription = '甲倉區'
WHERE code = 'DEMO-A';

UPDATE warehouse
SET store_id = '4F', warehouse = 'W202', area = '#B', slot = '02', stockTakeSection = 'B', stockTakeSectionDescription = '乙倉區'
WHERE code = 'DEMO-B';

INSERT INTO settings (name, value, category, type)
SELECT 'DO.floor.suppliers.2F', 'DEMO-SUP', 'DO', 'csv'
WHERE NOT EXISTS (SELECT 1 FROM settings WHERE name = 'DO.floor.suppliers.2F');

INSERT INTO settings (name, value, category, type)
SELECT 'DO.floor.suppliers.4F', 'DEMO-SUP', 'DO', 'csv'
WHERE NOT EXISTS (SELECT 1 FROM settings WHERE name = 'DO.floor.suppliers.4F');

INSERT INTO item_category (code, parent, sub, temperature)
SELECT 'DEMO-CAT-BAKE', '食品', '烘焙', '常溫'
WHERE NOT EXISTS (SELECT 1 FROM item_category WHERE code = 'DEMO-CAT-BAKE');

INSERT INTO item_category (code, parent, sub, temperature)
SELECT 'DEMO-CAT-DAIRY', '食品', '乳品', '冷藏'
WHERE NOT EXISTS (SELECT 1 FROM item_category WHERE code = 'DEMO-CAT-DAIRY');

SET @catBake := (SELECT id FROM item_category WHERE code = 'DEMO-CAT-BAKE' LIMIT 1);
SET @catDairy := (SELECT id FROM item_category WHERE code = 'DEMO-CAT-DAIRY' LIMIT 1);

UPDATE items SET categoryId = @catBake WHERE code IN ('DEMO-RM-01', 'DEMO-SF-01', 'DEMO-FG-01', 'DEMO-FG-02') AND categoryId IS NULL;
UPDATE items SET categoryId = @catDairy WHERE code = 'DEMO-RM-02' AND categoryId IS NULL;

INSERT INTO shop (code, name, type, contactName, contactNo, addr1, district, m18Id, m18LastModifyDate)
SELECT 'DEMO-SUP2', '東岸糖廠', 'supplier', '陳小姐', '21110002', '示範大道 8 號', '九龍', 910031, NOW()
WHERE NOT EXISTS (SELECT 1 FROM shop WHERE code = 'DEMO-SUP2');

INSERT INTO shop (code, name, type, contactName, contactNo, addr1, district, m18Id, m18LastModifyDate)
SELECT 'DEMO-SHP2', '南風麵包店', 'shop', '林先生', '51110002', '海旁街 18 號', '港島', 910032, NOW()
WHERE NOT EXISTS (SELECT 1 FROM shop WHERE code = 'DEMO-SHP2');

INSERT INTO warehouse (code, name, description, capacity, `order`, warehouse, area, slot, store_id, stockTakeSection, stockTakeSectionDescription)
SELECT 'DEMO-C', '丙倉', '示範貨架丙', 800, '3', 'W203', '#C', '03', '2F', 'C', '丙倉區'
WHERE NOT EXISTS (SELECT 1 FROM warehouse WHERE code = 'DEMO-C');

INSERT INTO user (username, password, locked, name, title, staffNo, lotusNotesUser)
SELECT 'demo.stock', u.password, 0, '示範倉務', '倉務', 'demo.stock', 0
FROM user u
WHERE u.username = 'demo'
AND NOT EXISTS (SELECT 1 FROM user x WHERE x.username = 'demo.stock');

INSERT INTO user (username, password, locked, name, title, staffNo, lotusNotesUser)
SELECT 'demo.prod', u.password, 0, '示範出品', '出品', 'demo.prod', 0
FROM user u
WHERE u.username = 'demo'
AND NOT EXISTS (SELECT 1 FROM user x WHERE x.username = 'demo.prod');

INSERT INTO user_authority (userId, authId)
SELECT u.id, a.id
FROM user u
JOIN authority a ON a.authority = 'STOCK'
WHERE u.username = 'demo.stock'
AND NOT EXISTS (
SELECT 1 FROM user_authority ua WHERE ua.userId = u.id AND ua.authId = a.id
);

INSERT INTO user_authority (userId, authId)
SELECT u.id, a.id
FROM user u
JOIN authority a ON a.authority IN ('PROD', 'PACK')
WHERE u.username = 'demo.prod'
AND NOT EXISTS (
SELECT 1 FROM user_authority ua WHERE ua.userId = u.id AND ua.authId = a.id
);

INSERT INTO equipment (code, name, description)
SELECT 'DEMO-EQ-01', '示範烤箱', '示範用熱風烤箱'
WHERE NOT EXISTS (SELECT 1 FROM equipment WHERE code = 'DEMO-EQ-01');

INSERT INTO equipment_detail (code, name, description, equipmentCode)
SELECT 'DEMO-EQD-01', '烤箱一號', '二樓示範烤箱', 'DEMO-EQ-01'
WHERE NOT EXISTS (SELECT 1 FROM equipment_detail WHERE code = 'DEMO-EQD-01');

INSERT INTO truck (TruckLanceCode, DepartureTime, shopId, ShopName, ShopCode, LoadingSequence, Store_id)
SELECT 'DEMO-AM', '08:00:00', @shp, '海港咖啡店', 'DEMO-SHP', 1, '2F'
WHERE NOT EXISTS (SELECT 1 FROM truck WHERE TruckLanceCode = 'DEMO-AM');

INSERT INTO truck (TruckLanceCode, DepartureTime, shopId, ShopName, ShopCode, LoadingSequence, Store_id)
SELECT 'DEMO-PM', '14:30:00', @shp, '海港咖啡店', 'DEMO-SHP', 2, '4F'
WHERE NOT EXISTS (SELECT 1 FROM truck WHERE TruckLanceCode = 'DEMO-PM');

INSERT INTO qc_category (code, name, isDefault)
SELECT 'DEMO-QC', '示範品質檢查', 1
WHERE NOT EXISTS (SELECT 1 FROM qc_category WHERE code = 'DEMO-QC');

SET @qcCat := (SELECT id FROM qc_category WHERE code = 'DEMO-QC' LIMIT 1);

INSERT INTO qc_item (code, name, description)
SELECT 'DEMO-QC-TEMP', '溫度', '入倉溫度'
WHERE NOT EXISTS (SELECT 1 FROM qc_item WHERE code = 'DEMO-QC-TEMP');

INSERT INTO qc_item (code, name, description)
SELECT 'DEMO-QC-LOOK', '外觀', '包裝與外觀'
WHERE NOT EXISTS (SELECT 1 FROM qc_item WHERE code = 'DEMO-QC-LOOK');

SET @qcTemp := (SELECT id FROM qc_item WHERE code = 'DEMO-QC-TEMP' LIMIT 1);
SET @qcLook := (SELECT id FROM qc_item WHERE code = 'DEMO-QC-LOOK' LIMIT 1);

INSERT INTO qc_item_category (qcItemId, qcCategoryId, `order`, description)
SELECT @qcTemp, @qcCat, 1, '溫度須低於 8 度'
WHERE NOT EXISTS (
SELECT 1 FROM qc_item_category WHERE qcItemId = @qcTemp AND qcCategoryId = @qcCat
);

INSERT INTO qc_item_category (qcItemId, qcCategoryId, `order`, description)
SELECT @qcLook, @qcCat, 2, '包裝完整'
WHERE NOT EXISTS (
SELECT 1 FROM qc_item_category WHERE qcItemId = @qcLook AND qcCategoryId = @qcCat
);

INSERT INTO items_qc_category_mapping (itemId, qcCategoryId, type)
SELECT @flour, @qcCat, 'IQC'
WHERE NOT EXISTS (
SELECT 1 FROM items_qc_category_mapping WHERE itemId = @flour AND type = 'IQC'
);

INSERT INTO items_qc_category_mapping (itemId, qcCategoryId, type)
SELECT @loaf, @qcCat, 'EPQC'
WHERE NOT EXISTS (
SELECT 1 FROM items_qc_category_mapping WHERE itemId = @loaf AND type = 'EPQC'
);

INSERT INTO items (code, name, type, description, categoryId, m18Id, m18LastModifyDate)
SELECT 'DEMO-MAT-01', '高筋麵粉', 'MAT', '報告用材料', @catBake, 910301, NOW()
WHERE NOT EXISTS (SELECT 1 FROM items WHERE code = 'DEMO-MAT-01');

INSERT INTO items (code, name, type, description, categoryId, m18Id, m18LastModifyDate)
SELECT 'DEMO-WIP-01', '發酵麵團', 'WIP', '報告用半成品', @catBake, 910302, NOW()
WHERE NOT EXISTS (SELECT 1 FROM items WHERE code = 'DEMO-WIP-01');

INSERT INTO items (code, name, type, description, categoryId, m18Id, m18LastModifyDate)
SELECT 'DEMO-FG-03', '牛油包', 'FG', '報告用成品', @catBake, 910303, NOW()
WHERE NOT EXISTS (SELECT 1 FROM items WHERE code = 'DEMO-FG-03');

INSERT INTO items (code, name, type, description, categoryId, m18Id, m18LastModifyDate)
SELECT 'DEMO-NM-01', '包裝袋', 'NM', '報告用非耗材', @catBake, 910304, NOW()
WHERE NOT EXISTS (SELECT 1 FROM items WHERE code = 'DEMO-NM-01');

INSERT INTO items (code, name, type, description, categoryId, m18Id, m18LastModifyDate)
SELECT 'DEMO-CMB-01', '早餐套餐', 'CMB', '報告用組合', @catBake, 910305, NOW()
WHERE NOT EXISTS (SELECT 1 FROM items WHERE code = 'DEMO-CMB-01');

SET @mat := (SELECT id FROM items WHERE code = 'DEMO-MAT-01' LIMIT 1);
SET @wip := (SELECT id FROM items WHERE code = 'DEMO-WIP-01' LIMIT 1);
SET @fg3 := (SELECT id FROM items WHERE code = 'DEMO-FG-03' LIMIT 1);
SET @nm := (SELECT id FROM items WHERE code = 'DEMO-NM-01' LIMIT 1);
SET @cmb := (SELECT id FROM items WHERE code = 'DEMO-CMB-01' LIMIT 1);

INSERT INTO item_uom (uomId, itemId, baseUnit, stockUnit, pickingUnit, salesUnit, purchaseUnit, ratioN, ratioD, price, currencyId, m18Id, m18LastModifyDate)
SELECT @kg, i.id, 1, 1, 1, 1, 1, 1, 1, 6.50, @cur, 910400 + i.id, NOW()
FROM items i
WHERE i.code IN ('DEMO-MAT-01', 'DEMO-WIP-01', 'DEMO-NM-01')
AND NOT EXISTS (SELECT 1 FROM item_uom iu WHERE iu.itemId = i.id AND iu.stockUnit = 1 AND iu.deleted = 0);

INSERT INTO item_uom (uomId, itemId, baseUnit, stockUnit, pickingUnit, salesUnit, purchaseUnit, ratioN, ratioD, price, currencyId, m18Id, m18LastModifyDate)
SELECT @pcs, i.id, 1, 1, 1, 1, 1, 1, 1, 12.00, @cur, 910500 + i.id, NOW()
FROM items i
WHERE i.code IN ('DEMO-FG-03', 'DEMO-CMB-01')
AND NOT EXISTS (SELECT 1 FROM item_uom iu WHERE iu.itemId = i.id AND iu.stockUnit = 1 AND iu.deleted = 0);

INSERT INTO bom (code, name, description, itemId, outputQty, outputQtyUom, type, status, m18Id, m18LastModifyDate)
SELECT 'DEMO-FG-01', '鄉村麵包', 'FG', @loaf, 10, 'PCS', 'FG', 'active', 910211, NOW()
WHERE NOT EXISTS (SELECT 1 FROM bom WHERE code = 'DEMO-FG-01');

INSERT INTO bom (code, name, description, itemId, outputQty, outputQtyUom, type, status, m18Id, m18LastModifyDate)
SELECT 'DEMO-FG-03', '牛油包', 'FG', @fg3, 12, 'PCS', 'FG', 'active', 910212, NOW()
WHERE NOT EXISTS (SELECT 1 FROM bom WHERE code = 'DEMO-FG-03');

INSERT INTO bom (code, name, description, itemId, outputQty, outputQtyUom, type, status, m18Id, m18LastModifyDate)
SELECT 'DEMO-WIP-01', '發酵麵團', 'WIP', @wip, 8, 'KG', 'WIP', 'active', 910213, NOW()
WHERE NOT EXISTS (SELECT 1 FROM bom WHERE code = 'DEMO-WIP-01');

UPDATE stock_in_line
SET dnNo = 'DEMO-DN-FL1', productionDate = DATE_SUB(NOW(), INTERVAL 3 DAY), productLotNo = 'PLOT-FL1'
WHERE lotNo = 'DEMO-LOT-FL1';

UPDATE stock_in_line
SET dnNo = 'DEMO-DN-MK1', productionDate = DATE_SUB(NOW(), INTERVAL 3 DAY), productLotNo = 'PLOT-MK1'
WHERE lotNo = 'DEMO-LOT-MK1';

UPDATE stock_in_line
SET dnNo = 'DEMO-DN-LF1', productionDate = DATE_SUB(NOW(), INTERVAL 2 DAY), productLotNo = 'PLOT-LF1'
WHERE lotNo = 'DEMO-LOT-LF1';

UPDATE stock_in_line
SET dnNo = 'DEMO-DN-LF2', productionDate = DATE_SUB(NOW(), INTERVAL 1 DAY)
WHERE lotNo = 'DEMO-LOT-LF2';

UPDATE purchase_order_line pol
JOIN items i ON i.id = pol.itemId
SET pol.up = CASE i.code WHEN 'DEMO-RM-01' THEN 8.50 WHEN 'DEMO-RM-02' THEN 15.00 ELSE pol.up END
WHERE pol.purchaseOrderId = @po3;

INSERT INTO m18_goods_receipt_note_log (m18_record_id, grn_code, stock_in_line_id, purchase_order_line_id, purchase_order_id, po_code, status, message)
SELECT 910501, 'DEMO-GRN-001', sil.id, sil.purchaseOrderLineId, sil.purchaseOrderId, 'DEMO-PO-003', 1, '示範入倉'
FROM stock_in_line sil
WHERE sil.lotNo = 'DEMO-LOT-FL1'
AND NOT EXISTS (SELECT 1 FROM m18_goods_receipt_note_log WHERE grn_code = 'DEMO-GRN-001');

INSERT INTO m18_goods_receipt_note_log (m18_record_id, grn_code, stock_in_line_id, purchase_order_line_id, purchase_order_id, po_code, status, message)
SELECT 910502, 'DEMO-GRN-002', sil.id, sil.purchaseOrderLineId, sil.purchaseOrderId, 'DEMO-PO-003', 1, '示範入倉'
FROM stock_in_line sil
WHERE sil.lotNo = 'DEMO-LOT-MK1'
AND NOT EXISTS (SELECT 1 FROM m18_goods_receipt_note_log WHERE grn_code = 'DEMO-GRN-002');

INSERT INTO qc_result (qcItemId, itemId, stockInLineId, failQty, type, remarks, qcPassed)
SELECT @qcLook, @flour, sil.id, 1.00, 'IQC', '包裝略有壓損', 0
FROM stock_in_line sil
WHERE sil.lotNo = 'DEMO-LOT-FL1'
AND NOT EXISTS (
SELECT 1 FROM qc_result qr WHERE qr.stockInLineId = sil.id AND qr.qcItemId = @qcLook
);

INSERT INTO qc_result (qcItemId, itemId, stockInLineId, failQty, type, remarks, qcPassed)
SELECT @qcTemp, @loaf, sil.id, 0.00, 'EPQC', '溫度正常', 1
FROM stock_in_line sil
WHERE sil.lotNo = 'DEMO-LOT-LF1'
AND NOT EXISTS (
SELECT 1 FROM qc_result qr WHERE qr.stockInLineId = sil.id AND qr.qcItemId = @qcTemp
);

INSERT INTO qc_result_measurement (qcResultId, qcType, measureType, value, unit)
SELECT qr.id, 'EPQC', '溫度', 6.50, 'C'
FROM qc_result qr
JOIN stock_in_line sil ON sil.id = qr.stockInLineId
WHERE sil.lotNo = 'DEMO-LOT-LF1' AND qr.qcItemId = @qcTemp
AND NOT EXISTS (SELECT 1 FROM qc_result_measurement m WHERE m.qcResultId = qr.id);

INSERT INTO delivery_order_pick_order (
shopId, storeId, requiredDeliveryDate, truckDepartureTime, truckLanceCode,
shopCode, shopName, loadingSequence, ticketNo, ticketReleaseTime, ticketCompleteDateTime,
ticketStatus, handledBy, handlerName, deliveryNoteCode
)
SELECT @shp, '2F', DATE_SUB(CURDATE(), INTERVAL 1 DAY), '08:00:00', 'DEMO-AM',
'DEMO-SHP', '海港咖啡店', 1, 'DEMO-TK-003', DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 20 HOUR),
'completed', @user, '示範倉務', 'DEMO-DN-DO3'
WHERE NOT EXISTS (SELECT 1 FROM delivery_order_pick_order WHERE ticketNo = 'DEMO-TK-003');

SET @dopo := (SELECT id FROM delivery_order_pick_order WHERE ticketNo = 'DEMO-TK-003' LIMIT 1);

UPDATE pick_order
SET deliveryOrderPickOrderId = @dopo
WHERE code = 'DEMO-PK-DO3' AND deliveryOrderPickOrderId IS NULL;

INSERT INTO stock_out (type, deliveryOrderCode, completeDate, status, handlerId)
SELECT 'do', 'DEMO-DO-003', DATE_SUB(NOW(), INTERVAL 1 DAY), 'completed', @user
WHERE NOT EXISTS (SELECT 1 FROM stock_out WHERE deliveryOrderCode = 'DEMO-DO-003' AND type = 'do');

INSERT INTO stock_out (type, consoPickOrderCode, completeDate, status, handlerId)
SELECT 'job', 'DEMO-PK-JO2', DATE_SUB(NOW(), INTERVAL 2 HOUR), 'completed', @user
WHERE NOT EXISTS (SELECT 1 FROM stock_out WHERE consoPickOrderCode = 'DEMO-PK-JO2' AND type = 'job');

SET @soDo := (SELECT id FROM stock_out WHERE deliveryOrderCode = 'DEMO-DO-003' AND type = 'do' LIMIT 1);
SET @soJo := (SELECT id FROM stock_out WHERE consoPickOrderCode = 'DEMO-PK-JO2' AND type = 'job' LIMIT 1);
SET @polDo := (SELECT pol.id FROM pick_order_line pol WHERE pol.poId = @pkDo3 LIMIT 1);
SET @polJo := (SELECT pol.id FROM pick_order_line pol JOIN items i ON i.id = pol.itemId WHERE pol.poId = @pkJo2 AND i.code = 'DEMO-RM-01' LIMIT 1);
SET @illLoaf := (
SELECT ill.id FROM inventory_lot_line ill
JOIN inventory_lot il ON il.id = ill.inventoryLotId
WHERE il.lotNo = 'DEMO-LOT-LF1' LIMIT 1
);
SET @illFlour := (
SELECT ill.id FROM inventory_lot_line ill
JOIN inventory_lot il ON il.id = ill.inventoryLotId
WHERE il.lotNo = 'DEMO-LOT-FL1' LIMIT 1
);

INSERT INTO stock_out_line (itemId, qty, stockOutId, pickOrderLineId, inventoryLotLineId, status, pickTime, handled_by, endTime)
SELECT @loaf, 6, @soDo, @polDo, @illLoaf, 'completed', DATE_SUB(NOW(), INTERVAL 1 DAY), @user, DATE_SUB(NOW(), INTERVAL 1 DAY)
WHERE NOT EXISTS (SELECT 1 FROM stock_out_line WHERE stockOutId = @soDo AND pickOrderLineId = @polDo);

INSERT INTO stock_out_line (itemId, qty, stockOutId, pickOrderLineId, inventoryLotLineId, status, pickTime, handled_by, endTime)
SELECT @flour, 5, @soJo, @polJo, @illFlour, 'completed', DATE_SUB(NOW(), INTERVAL 2 HOUR), @user, DATE_SUB(NOW(), INTERVAL 2 HOUR)
WHERE NOT EXISTS (SELECT 1 FROM stock_out_line WHERE stockOutId = @soJo AND pickOrderLineId = @polJo);

INSERT INTO do_pick_order_record (
record_id, pick_order_id, do_order_id, shop_id, store_id, RequiredDeliveryDate,
TruckLanceCode, ShopCode, ShopName, delivery_order_code, deliveryNoteCode, pick_order_code,
ticket_no, ticket_release_time, ticketCompleteDateTime, ticket_status, handled_by, handler_name, deleted
)
SELECT 0, @pkDo3, @do3, @shp, '2F', DATE_SUB(CURDATE(), INTERVAL 1 DAY),
'DEMO-AM', 'DEMO-SHP', '海港咖啡店', 'DEMO-DO-003', 'DEMO-DN-DO3', 'DEMO-PK-DO3',
'DEMO-TK-003', DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 20 HOUR), 'completed', @user, '示範倉務', 0
WHERE NOT EXISTS (SELECT 1 FROM do_pick_order_record WHERE ticket_no = 'DEMO-TK-003');

UPDATE do_pick_order_record SET record_id = id WHERE ticket_no = 'DEMO-TK-003' AND record_id = 0;

SET @dpor := (SELECT id FROM do_pick_order_record WHERE ticket_no = 'DEMO-TK-003' LIMIT 1);

INSERT INTO do_pick_order_line_record (
record_id, do_pick_order_id, pick_order_id, do_order_id, pick_order_code, delivery_order_code, status, deleted
)
SELECT @dpor, @dopo, @pkDo3, @do3, 'DEMO-PK-DO3', 'DEMO-DO-003', 'correct', 0
WHERE @dpor IS NOT NULL
AND NOT EXISTS (SELECT 1 FROM do_pick_order_line_record WHERE record_id = @dpor AND do_order_id = @do3);

INSERT INTO stock_ledger (date, inventoryId, itemId, itemCode, inQty, outQty, balance, type, modified)
SELECT DATE_SUB(CURDATE(), INTERVAL 20 DAY), inv.id, inv.itemId, i.code, inv.onHandQty, 0, inv.onHandQty, 'IN', DATE_SUB(NOW(), INTERVAL 20 DAY)
FROM inventory inv
JOIN items i ON i.id = inv.itemId
WHERE i.code IN ('DEMO-RM-01', 'DEMO-RM-02', 'DEMO-FG-01')
AND NOT EXISTS (
SELECT 1 FROM stock_ledger sl WHERE sl.itemCode = i.code AND sl.type = 'IN' AND sl.deleted = 0
);

INSERT INTO stock_ledger (date, inventoryId, itemId, itemCode, inQty, outQty, balance, type, stockOutLineId, modified)
SELECT DATE_SUB(CURDATE(), INTERVAL 1 DAY), inv.id, @loaf, 'DEMO-FG-01', 0, 6, inv.onHandQty, 'DO', sol.id, DATE_SUB(NOW(), INTERVAL 1 DAY)
FROM inventory inv
JOIN stock_out_line sol ON sol.stockOutId = @soDo
WHERE inv.itemId = @loaf
AND NOT EXISTS (SELECT 1 FROM stock_ledger sl WHERE sl.itemCode = 'DEMO-FG-01' AND sl.type = 'DO' AND sl.deleted = 0);

INSERT INTO stock_ledger (date, inventoryId, itemId, itemCode, inQty, outQty, balance, type, stockOutLineId, modified)
SELECT CURDATE(), inv.id, @flour, 'DEMO-RM-01', 0, 5, inv.onHandQty, 'JO', sol.id, DATE_SUB(NOW(), INTERVAL 2 HOUR)
FROM inventory inv
JOIN stock_out_line sol ON sol.stockOutId = @soJo
WHERE inv.itemId = @flour
AND NOT EXISTS (SELECT 1 FROM stock_ledger sl WHERE sl.itemCode = 'DEMO-RM-01' AND sl.type = 'JO' AND sl.deleted = 0);

INSERT INTO stock_in (code, supplierId, shopId, m18DataLogId, orderDate, completeDate, status)
SELECT 'DEMO-SI-RPT', @sup, @shp, @log, DATE_SUB(NOW(), INTERVAL 40 DAY), DATE_SUB(NOW(), INTERVAL 39 DAY), 'completed'
WHERE NOT EXISTS (SELECT 1 FROM stock_in WHERE code = 'DEMO-SI-RPT');

SET @siRpt := (SELECT id FROM stock_in WHERE code = 'DEMO-SI-RPT' LIMIT 1);

INSERT INTO stock_in_line (itemId, itemNo, stockInId, demandQty, acceptedQty, status, lotNo, expiryDate, receiptDate, productionDate, productLotNo, dnNo)
SELECT i.id, i.code, @siRpt, 20, 20, 'completed', CONCAT('DEMO-LOT-', i.code), DATE_ADD(CURDATE(), INTERVAL 90 DAY), DATE_SUB(NOW(), INTERVAL 39 DAY), DATE_SUB(NOW(), INTERVAL 40 DAY), CONCAT('PLOT-', i.code), CONCAT('DN-', i.code)
FROM items i
WHERE i.code IN ('DEMO-MAT-01', 'DEMO-WIP-01', 'DEMO-FG-03', 'DEMO-NM-01', 'DEMO-CMB-01')
AND NOT EXISTS (SELECT 1 FROM stock_in_line sil WHERE sil.itemId = i.id AND sil.stockInId = @siRpt);

INSERT INTO inventory_lot (itemId, stockInLineId, expiryDate, lotNo, stockInDate, productionDate)
SELECT sil.itemId, sil.id, sil.expiryDate, sil.lotNo, sil.receiptDate, sil.productionDate
FROM stock_in_line sil
WHERE sil.stockInId = @siRpt
AND NOT EXISTS (SELECT 1 FROM inventory_lot il WHERE il.stockInLineId = sil.id);

UPDATE stock_in_line sil
JOIN inventory_lot il ON il.stockInLineId = sil.id
SET sil.inventoryLotId = il.id
WHERE sil.stockInId = @siRpt AND sil.inventoryLotId IS NULL;

INSERT INTO inventory_lot_line (inventoryLotId, warehouseId, stockItemUomId, inQty, outQty, holdQty, status)
SELECT il.id, @whA, iu.id, 20, 0, 0, 'available'
FROM inventory_lot il
JOIN stock_in_line sil ON sil.id = il.stockInLineId
JOIN item_uom iu ON iu.itemId = il.itemId AND iu.stockUnit = 1 AND iu.deleted = 0
WHERE sil.stockInId = @siRpt
AND NOT EXISTS (SELECT 1 FROM inventory_lot_line ill WHERE ill.inventoryLotId = il.id);

INSERT INTO stock_ledger (date, inventoryId, itemId, itemCode, inQty, outQty, balance, type, modified)
SELECT DATE_SUB(CURDATE(), INTERVAL 30 DAY), inv.id, inv.itemId, i.code, 20, 0, 20, 'IN', DATE_SUB(NOW(), INTERVAL 30 DAY)
FROM inventory inv
JOIN items i ON i.id = inv.itemId
WHERE i.code IN ('DEMO-MAT-01', 'DEMO-WIP-01', 'DEMO-FG-03', 'DEMO-NM-01', 'DEMO-CMB-01')
AND NOT EXISTS (SELECT 1 FROM stock_ledger sl WHERE sl.itemCode = i.code AND sl.type = 'IN' AND sl.deleted = 0);

INSERT INTO stock_ledger (date, inventoryId, itemId, itemCode, inQty, outQty, balance, type, modified)
SELECT STR_TO_DATE(CONCAT(YEAR(CURDATE()), '-01-15'), '%Y-%m-%d'), inv.id, i.id, i.code, 0, 4, 16, 'OUT', STR_TO_DATE(CONCAT(YEAR(CURDATE()), '-01-15 10:00:00'), '%Y-%m-%d %H:%i:%s')
FROM items i
JOIN inventory inv ON inv.itemId = i.id
WHERE i.code IN ('DEMO-MAT-01', 'DEMO-WIP-01', 'DEMO-FG-03', 'DEMO-NM-01', 'DEMO-CMB-01')
AND NOT EXISTS (SELECT 1 FROM stock_ledger sl WHERE sl.itemCode = i.code AND sl.type = 'OUT' AND MONTH(sl.modified) = 1);

INSERT INTO stock_ledger (date, inventoryId, itemId, itemCode, inQty, outQty, balance, type, modified)
SELECT STR_TO_DATE(CONCAT(YEAR(CURDATE()), '-04-15'), '%Y-%m-%d'), inv.id, i.id, i.code, 0, 6, 10, 'OUT', STR_TO_DATE(CONCAT(YEAR(CURDATE()), '-04-15 10:00:00'), '%Y-%m-%d %H:%i:%s')
FROM items i
JOIN inventory inv ON inv.itemId = i.id
WHERE i.code IN ('DEMO-MAT-01', 'DEMO-WIP-01', 'DEMO-FG-03', 'DEMO-NM-01', 'DEMO-CMB-01')
AND NOT EXISTS (SELECT 1 FROM stock_ledger sl WHERE sl.itemCode = i.code AND sl.type = 'OUT' AND MONTH(sl.modified) = 4);

INSERT INTO stock_ledger (date, inventoryId, itemId, itemCode, inQty, outQty, balance, type, modified)
SELECT STR_TO_DATE(CONCAT(YEAR(CURDATE()), '-07-15'), '%Y-%m-%d'), inv.id, i.id, i.code, 0, 3, 7, 'OUT', STR_TO_DATE(CONCAT(YEAR(CURDATE()), '-07-15 10:00:00'), '%Y-%m-%d %H:%i:%s')
FROM items i
JOIN inventory inv ON inv.itemId = i.id
WHERE i.code IN ('DEMO-MAT-01', 'DEMO-WIP-01', 'DEMO-FG-03', 'DEMO-NM-01', 'DEMO-CMB-01')
AND NOT EXISTS (SELECT 1 FROM stock_ledger sl WHERE sl.itemCode = i.code AND sl.type = 'OUT' AND MONTH(sl.modified) = 7);

INSERT INTO stock_ledger (date, inventoryId, itemId, itemCode, inQty, outQty, balance, type, modified)
SELECT STR_TO_DATE(CONCAT(YEAR(CURDATE()), '-09-10'), '%Y-%m-%d'), inv.id, i.id, i.code, 0, 5, 2, 'OUT', STR_TO_DATE(CONCAT(YEAR(CURDATE()), '-09-10 10:00:00'), '%Y-%m-%d %H:%i:%s')
FROM items i
JOIN inventory inv ON inv.itemId = i.id
WHERE i.code IN ('DEMO-MAT-01', 'DEMO-WIP-01', 'DEMO-FG-03', 'DEMO-NM-01', 'DEMO-CMB-01')
AND NOT EXISTS (SELECT 1 FROM stock_ledger sl WHERE sl.itemCode = i.code AND sl.type = 'OUT' AND MONTH(sl.modified) = 9);

INSERT INTO stock_take (code, planStart, planEnd, actualStart, actualEnd, status, stockTakeRoundId, stockTakeRoundName, stockTakeSection)
SELECT 'DEMO-ST-001', DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 2 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY), 'completed', 9001, '示範盤點輪次', 'A'
WHERE NOT EXISTS (SELECT 1 FROM stock_take WHERE code = 'DEMO-ST-001');

SET @st := (SELECT id FROM stock_take WHERE code = 'DEMO-ST-001' LIMIT 1);
SET @lotFl := (SELECT id FROM inventory_lot WHERE lotNo = 'DEMO-LOT-FL1' LIMIT 1);
SET @lotMk := (SELECT id FROM inventory_lot WHERE lotNo = 'DEMO-LOT-MK1' LIMIT 1);
SET @lotLf := (SELECT id FROM inventory_lot WHERE lotNo = 'DEMO-LOT-LF1' LIMIT 1);

INSERT INTO stocktakerecord (
itemId, lotId, warehouseId, stockTakeId, stockTakeRoundId, stockTakerId, stockTakerName,
approverId, approverName, bookQty, approverStockTakeQty, varianceQty, uom, date, status,
approverTime, itemCode, itemName, lotNo, expiredDate
)
SELECT @flour, @lotFl, @whA, @st, 9001, @user, '示範倉務', @user, '示範管理員', 40, 38, -2, '公斤', DATE_SUB(CURDATE(), INTERVAL 1 DAY), 'completed', DATE_SUB(NOW(), INTERVAL 1 DAY), 'DEMO-RM-01', '普通麵粉', 'DEMO-LOT-FL1', DATE_ADD(CURDATE(), INTERVAL 180 DAY)
WHERE NOT EXISTS (SELECT 1 FROM stocktakerecord WHERE stockTakeId = @st AND lotId = @lotFl);

INSERT INTO stocktakerecord (
itemId, lotId, warehouseId, stockTakeId, stockTakeRoundId, stockTakerId, stockTakerName,
bookQty, approverStockTakeQty, varianceQty, uom, date, status, itemCode, itemName, lotNo
)
SELECT @milk, @lotMk, @whA, @st, 9001, @user, '示範倉務', 20, 20, 0, '公斤', DATE_SUB(CURDATE(), INTERVAL 1 DAY), 'completed', 'DEMO-RM-02', '鮮奶', 'DEMO-LOT-MK1'
WHERE NOT EXISTS (SELECT 1 FROM stocktakerecord WHERE stockTakeId = @st AND lotId = @lotMk);

INSERT INTO stocktakerecord (
itemId, lotId, warehouseId, stockTakeId, stockTakeRoundId, stockTakerId, stockTakerName,
bookQty, uom, date, status, itemCode, itemName, lotNo
)
SELECT @loaf, @lotLf, @whB, @st, 9001, @user, '示範倉務', 30, '件', CURDATE(), 'pending', 'DEMO-FG-01', '鄉村麵包', 'DEMO-LOT-LF1'
WHERE NOT EXISTS (SELECT 1 FROM stocktakerecord WHERE stockTakeId = @st AND lotId = @lotLf);

INSERT INTO delivery_order (code, shopId, supplierId, currencyId, orderDate, estimatedArrivalDate, status, m18DataLogId, m18BeId, isExtra)
SELECT 'DEMO-DO-004', @shp, @sup, currencyId, NOW(), CURDATE(), 'pending', m18DataLogId, m18BeId, 0
FROM delivery_order
WHERE code = 'DEMO-DO-001'
AND NOT EXISTS (SELECT 1 FROM delivery_order WHERE code = 'DEMO-DO-004');

SET @do4 := (SELECT id FROM delivery_order WHERE code = 'DEMO-DO-004' LIMIT 1);

INSERT INTO delivery_order_line (deliveryOrderId, itemId, itemNo, uomId, uomIdM18, qty, qtyM18, status, m18DataLogId)
SELECT @do4, @loaf, 'DEMO-FG-01', @kg, @kg, 3, 3, 'pending', @log
WHERE @do4 IS NOT NULL
AND NOT EXISTS (SELECT 1 FROM delivery_order_line WHERE deliveryOrderId = @do4);

SET @doLine3 := (
SELECT dol.id FROM delivery_order_line dol WHERE dol.deliveryOrderId = @do3 LIMIT 1
);

INSERT INTO do_replenishment (
code, deliveryDate, sourceDoId, sourceDoCode, sourceDoLineId, sourceM18DataLogId, sourceM18Id,
itemId, itemNo, itemName, replenishQty, uomId, shopId, shopCode, shopName, truckLaneCode,
status, reason, deliveryOrderPickOrderId, pickOrderLineId
)
SELECT 'DEMO-REPL-001', DATE_SUB(CURDATE(), INTERVAL 1 DAY), @do3, 'DEMO-DO-003', @doLine3, @log, 910601,
@loaf, 'DEMO-FG-01', '鄉村麵包', 2, @pcs, @shp, 'DEMO-SHP', '海港咖啡店', 'DEMO-AM',
'completed', '示範補貨', @dopo, @polDo
WHERE NOT EXISTS (SELECT 1 FROM do_replenishment WHERE code = 'DEMO-REPL-001');

INSERT INTO m18_bom_shop_sync_log (bom_id, finished_item_code, m18_header_code, m18_api_status, synced, message)
SELECT @bom, 'DEMO-FG-01', 'DEMO-BOM-HDR', 1, 1, '示範同步成功'
WHERE NOT EXISTS (SELECT 1 FROM m18_bom_shop_sync_log WHERE finished_item_code = 'DEMO-FG-01' AND synced = 1);

INSERT INTO m18_bom_shop_sync_log (bom_id, finished_item_code, m18_header_code, m18_api_status, synced, message)
SELECT b.id, 'DEMO-FG-03', 'DEMO-BOM-HDR-2', 0, 0, '示範同步失敗'
FROM bom b
WHERE b.code = 'DEMO-FG-03'
AND NOT EXISTS (SELECT 1 FROM m18_bom_shop_sync_log WHERE finished_item_code = 'DEMO-FG-03' AND synced = 0);

INSERT INTO settings (name, value, category, type)
SELECT 'DEMO.coverage.v1', 'loaded', 'DEMO', 'marker'
WHERE NOT EXISTS (SELECT 1 FROM settings WHERE name = 'DEMO.coverage.v1');

+ 1
- 1
src/main/resources/jasper/SemiFGProductionAnalysisReport.jrxml Переглянути файл

@@ -33,7 +33,7 @@
<parameterDescription><![CDATA["lastOutDateStart"]]></parameterDescription>
</parameter>
<queryString>
<![CDATA[select * from fpsmsdb.items , fpsmsdb.stock_out_line]]>
<![CDATA[select * from items , stock_out_line]]>
</queryString>
<field name="id" class="java.lang.Integer">
<property name="com.jaspersoft.studio.field.name" value="id"/>


+ 1
- 1
src/main/resources/jasper/StockItemConsumptionTrendReport.jrxml Переглянути файл

@@ -33,7 +33,7 @@
<parameterDescription><![CDATA["lastOutDateStart"]]></parameterDescription>
</parameter>
<queryString>
<![CDATA[select * from fpsmsdb.items , fpsmsdb.stock_out_line]]>
<![CDATA[select * from items , stock_out_line]]>
</queryString>
<field name="id" class="java.lang.Integer">
<property name="com.jaspersoft.studio.field.name" value="id"/>


Завантаження…
Відмінити
Зберегти