|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- package com.ffii.fpsms.m18.service
-
- import com.fasterxml.jackson.core.JsonGenerator
- import com.fasterxml.jackson.databind.JsonNode
- import com.fasterxml.jackson.databind.ObjectMapper
- import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
- import com.ffii.fpsms.api.service.ApiCallerService
- import com.ffii.fpsms.m18.M18Config
- import com.ffii.fpsms.m18.model.GoodsReceiptNoteRequest
- import com.ffii.fpsms.m18.model.GoodsReceiptNoteResponse
- import org.slf4j.Logger
- import org.slf4j.LoggerFactory
- import org.springframework.stereotype.Service
- import org.springframework.util.LinkedMultiValueMap
- import reactor.core.publisher.Mono
-
- /**
- * Service to create Goods Receipt Note (AN) in M18 via the save API.
- * API: PUT http://[server]/jsf/rfws/root/api/save/an
- * Query: menuCode=an (required), param (optional, JSON string)
- * Headers: authorization (Bearer token), client_id
- */
- @Service
- open class M18GoodsReceiptNoteService(
- private val m18Config: M18Config,
- private val apiCallerService: ApiCallerService,
- ) {
- private val logger: Logger = LoggerFactory.getLogger(M18GoodsReceiptNoteService::class.java)
-
- private val M18_SAVE_GOODS_RECEIPT_NOTE_API = "/root/api/save/an"
- private val M18_READ_GOODS_RECEIPT_NOTE_API = "/root/api/read/an"
- private val MENU_CODE_AN = "an"
-
- // Serialize with UTF-8 characters (no \\uXXXX) so M18 persists bDesc/bDesc_en like Postman
- @Suppress("DEPRECATION")
- private val grnRequestMapper: ObjectMapper = jacksonObjectMapper().apply {
- disable(JsonGenerator.Feature.ESCAPE_NON_ASCII)
- }
-
- /**
- * Creates a goods receipt note in M18.
- *
- * @param request The request body containing mainan (header) and ant (lines).
- * @param param Optional extra parameters in JSON format (query param "param").
- * @return The M18 response with recordId, messages, and status; or null on failure.
- */
- open fun createGoodsReceiptNote(
- request: GoodsReceiptNoteRequest,
- param: String? = null,
- ): GoodsReceiptNoteResponse? {
- return createGoodsReceiptNoteMono(request, param).block()
- }
-
- /**
- * Creates a goods receipt note in M18 (reactive).
- *
- * @param request The request body containing mainan (header) and ant (lines).
- * @param param Optional extra parameters in JSON format (query param "param").
- * @return Mono of the M18 response.
- */
- open fun createGoodsReceiptNoteMono(
- request: GoodsReceiptNoteRequest,
- param: String? = null,
- ): Mono<GoodsReceiptNoteResponse> {
- val queryParams = LinkedMultiValueMap<String, String>().apply {
- add("menuCode", MENU_CODE_AN)
- param?.let { add("param", it) }
- }
- val queryString = queryParams.entries.flatMap { (k, v) -> v.map { value -> "$k=$value" } }.joinToString("&")
- val fullUrl = "${m18Config.BASE_URL}$M18_SAVE_GOODS_RECEIPT_NOTE_API?$queryString"
- val requestJson = grnRequestMapper.writeValueAsString(request)
- logger.info("[M18 GRN API] call=PUT url=$fullUrl body=$requestJson")
- return apiCallerService.putWithJsonString<GoodsReceiptNoteResponse>(
- urlPath = M18_SAVE_GOODS_RECEIPT_NOTE_API,
- queryParams = queryParams,
- bodyJson = requestJson,
- ).doOnSuccess { response ->
- val responseJson = grnRequestMapper.writeValueAsString(response)
- logger.info("[M18 GRN API] response (all): $responseJson")
- if (response.status) {
- logger.info("Goods receipt note created in M18. recordId=${response.recordId}")
- } else {
- logger.warn("M18 save AN returned status=false. recordId=${response.recordId}, messages=${response.messages}")
- }
- }.doOnError { e ->
- logger.error("[M18 GRN API] call failed: $fullUrl error=${e.message}", e)
- }
- }
-
- /**
- * Reads AN (GRN) from M18 by record id. GET /root/api/read/an?id={recordId}
- * Document code is typically at data.mainan.values[0].code or data.mainan.code.
- */
- open fun readAnCodeByRecordId(recordId: Long): String? {
- return try {
- val node: JsonNode = apiCallerService.get<JsonNode>(
- M18_READ_GOODS_RECEIPT_NOTE_API,
- mapOf("id" to recordId),
- ).block() ?: return null
- extractAnCodeFromReadResponse(node)
- } catch (e: Exception) {
- logger.warn("[M18 read AN] id=$recordId failed: ${e.message}", e)
- null
- }
- }
-
- private fun extractAnCodeFromReadResponse(node: JsonNode): String? {
- val paths = listOf(
- "/data/mainan/0/code",
- "/data/mainan/values/0/code",
- "/data/mainan/code",
- "/data/ant/0/code",
- "/mainan/values/0/code",
- "/mainan/code",
- "/mainan/0/code",
- )
- for (p in paths) {
- val v = node.at(p)
- if (!v.isMissingNode && !v.isNull) {
- val text = v.asText()
- if (!text.isNullOrBlank()) return text
- }
- }
- return null
- }
- }
|