diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 5c90718..af155e6 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -52,7 +52,7 @@ jobs: run: | sudo apt-get update && sudo apt-get install -y sqlite3 sqlite3 test.db "CREATE TABLE item (id INTEGER PRIMARY KEY, barcode TEXT, name TEXT, price INTEGER); - CREATE TABLE sale (id INTEGER PRIMARY KEY, storeId INTEGER, staffId INTEGER, quantity INTEGER, amount INTEGER, deposit INTEGER, createdAt TEXT); + CREATE TABLE sale (id INTEGER PRIMARY KEY, storeId INTEGER, quantity INTEGER, amount INTEGER, deposit INTEGER, createdAt TEXT); CREATE TABLE sale_detail (id INTEGER PRIMARY KEY, saleId INTEGER, itemId INTEGER, price INTEGER, quantity INTEGER); INSERT INTO item VALUES (1, 'test-barcode', 'Test Item', 100); INSERT INTO item VALUES (2, 'test-barcode-2', 'Test Item 2', 200);" diff --git a/api.yaml b/api.yaml index b1c63e7..b03ac49 100644 --- a/api.yaml +++ b/api.yaml @@ -18,14 +18,10 @@ tags: description: 商品管理 - name: Sales description: 売上管理 - - name: Staff - description: スタッフ管理 - name: Stores description: 店舗管理 - name: Settings description: 設定管理 - - name: Users - description: ユーザー管理 paths: # Items @@ -269,9 +265,6 @@ paths: change: type: integer description: おつり - staffId: - type: integer - description: スタッフID storeId: type: integer description: 店舗ID @@ -335,113 +328,6 @@ paths: isValid: type: boolean - # Staff - /api/staff: - get: - tags: - - Staff - summary: スタッフ一覧取得 - description: 全スタッフを取得します - operationId: getAllStaff - responses: - '200': - description: Success - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/StaffEntity' - - post: - tags: - - Staff - summary: スタッフ登録 - description: 新しいスタッフを登録します - operationId: createStaff - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateStaffRequest' - responses: - '201': - description: Created - content: - application/json: - schema: - $ref: '#/components/schemas/StaffEntity' - - /api/staff/{barcode}: - get: - tags: - - Staff - summary: スタッフ取得 - description: バーコードからスタッフを取得します - operationId: getStaffByBarcode - parameters: - - name: barcode - in: path - required: true - schema: - type: string - responses: - '200': - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/StaffEntity' - '404': - description: Staff not found - - put: - tags: - - Staff - summary: スタッフ更新 - description: スタッフ情報を更新します - operationId: updateStaff - parameters: - - name: barcode - in: path - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateStaffRequest' - responses: - '200': - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/StaffEntity' - '404': - description: Staff not found - - delete: - tags: - - Staff - summary: スタッフ削除 - description: スタッフを削除します - operationId: deleteStaff - parameters: - - name: barcode - in: path - required: true - schema: - type: string - responses: - '204': - description: No Content - '404': - description: Staff not found - # Stores /api/stores: get: @@ -797,46 +683,6 @@ paths: message: type: string - # Users - /api/users: - get: - tags: - - Users - summary: ユーザー一覧取得 - description: 全ユーザーを取得します - operationId: getAllUsers - responses: - '200': - description: Success - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/StaffEntity' - - /api/users/{barcode}: - get: - tags: - - Users - summary: ユーザー取得 - description: バーコードからユーザーを取得します - operationId: getUserByBarcode - parameters: - - name: barcode - in: path - required: true - schema: - type: string - responses: - '200': - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/StaffEntity' - '404': - description: User not found components: schemas: @@ -896,12 +742,6 @@ components: storeName: type: string description: 店舗名 - staffId: - type: string - description: スタッフID - staffName: - type: string - description: スタッフ名 totalAmount: type: integer description: 合計金額(リバー) @@ -924,8 +764,6 @@ components: - id - storeId - storeName - - staffId - - staffName - totalAmount - deposit - change @@ -966,10 +804,6 @@ components: storeId: type: integer description: 店舗ID - staffBarcode: - type: string - nullable: true - description: スタッフバーコード(オプショナル) itemIds: type: string description: 商品IDのカンマ区切り文字列(例:"1,2,3") @@ -983,43 +817,6 @@ components: - itemIds - deposit - StaffEntity: - type: object - properties: - barcode: - type: string - example: "STAFF001" - name: - type: string - example: "田中太郎" - required: - - barcode - - name - - CreateStaffRequest: - type: object - properties: - barcode: - type: string - minLength: 1 - maxLength: 50 - name: - type: string - minLength: 1 - maxLength: 100 - required: - - barcode - - name - - UpdateStaffRequest: - type: object - properties: - name: - type: string - minLength: 1 - maxLength: 100 - required: - - name StoreEntity: type: object @@ -1093,4 +890,4 @@ components: # セキュリティは現在未実装のため、コメントアウト # security: -# - bearerAuth: [] \ No newline at end of file +# - bearerAuth: [] diff --git a/src/main/kotlin/info/nukoneko/kidspos/common/Constants.kt b/src/main/kotlin/info/nukoneko/kidspos/common/Constants.kt index e3adef2..3d27692 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/common/Constants.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/common/Constants.kt @@ -10,6 +10,17 @@ object Constants { object Barcode { const val SUFFIX_LENGTH = 3 const val MIN_LENGTH = 4 + const val TOTAL_LENGTH = 10 + const val ID_LENGTH = 6 + + // バーコード種別コード + const val TYPE_STAFF = "00" + const val TYPE_ITEM = "01" + const val TYPE_SALE = "02" + + // バーコードの開始・終了記号 + const val PREFIX = "A" + const val SUFFIX = "A" } /** @@ -25,7 +36,11 @@ object Constants { */ object Validation { const val NAME_MAX_LENGTH = 255 - const val BARCODE_PATTERN = "^[0-9]{4,}$" + + // 独自バーコードフォーマット: A + 種別(2桁) + ID(6桁) + A + // 種別: 00(STAFF), 01(ITEM), 02(SALE) + // 例: A01000001A (商品ID:1) + const val BARCODE_PATTERN = "^A(00|01|02)\\d{6}A$" const val MAX_PRICE = 1000000 const val MAX_QUANTITY = 9999 } diff --git a/src/main/kotlin/info/nukoneko/kidspos/receipt/ReceiptDetail.kt b/src/main/kotlin/info/nukoneko/kidspos/receipt/ReceiptDetail.kt index c3efc5f..7755fb6 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/receipt/ReceiptDetail.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/receipt/ReceiptDetail.kt @@ -6,7 +6,6 @@ import java.util.* data class ReceiptDetail( val items: List, val storeName: String?, - val staffName: String?, val deposit: Int, val transactionId: String?, val createdAt: Date, diff --git a/src/main/kotlin/info/nukoneko/kidspos/receipt/ReceiptPrinter.kt b/src/main/kotlin/info/nukoneko/kidspos/receipt/ReceiptPrinter.kt index 321dc9a..1ba91b9 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/receipt/ReceiptPrinter.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/receipt/ReceiptPrinter.kt @@ -22,14 +22,11 @@ class ReceiptPrinter( command.writeTextLine(dateFormat.format(detail.createdAt)) command.newLine() - // 店舗名・担当者 + // 店舗名 command.setGravity(PrintCommand.Direction.LEFT) if (!detail.storeName.isNullOrEmpty()) { command.writeTextLine("店舗名:${detail.storeName.toAllEm()}") } - if (!detail.staffName.isNullOrEmpty()) { - command.writeTextLine("担 当:${detail.staffName.toAllEm()}") - } command.drawLine() command.setGravity(PrintCommand.Direction.CENTER) diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/ItemApiController.kt b/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/ItemApiController.kt index 5b44768..d42b6e0 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/ItemApiController.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/ItemApiController.kt @@ -230,7 +230,7 @@ class ItemApiController { val headers = HttpHeaders().apply { contentType = MediaType.APPLICATION_PDF - setContentDispositionFormData("inline", "barcodes.pdf") + setContentDispositionFormData("attachment", "barcodes.pdf") } logger.info("Barcode PDF generated successfully with {} items", items.size) diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/SaleApiController.kt b/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/SaleApiController.kt index 8281ed2..a02dc14 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/SaleApiController.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/SaleApiController.kt @@ -45,17 +45,15 @@ class SaleApiController( val saleBean = SaleBean( storeId = request.storeId, - staffBarcode = request.staffBarcode, itemIds = request.itemIds, deposit = request.deposit, ) when (val result = saleProcessingService.processSaleWithValidation(saleBean, items)) { is SaleResult.Success -> { - // Print receipt (staffBarcode is now nullable) + // Print receipt receiptService.printReceipt( request.storeId, items, - request.staffBarcode ?: "", request.deposit, ) @@ -67,7 +65,6 @@ class SaleApiController( "quantity" to sale.quantity, "deposit" to request.deposit, "change" to (request.deposit - sale.amount), - "staffId" to sale.staffId, "storeId" to sale.storeId, ) logger.info("Sale created successfully: ID={}", sale.id) @@ -108,11 +105,10 @@ class SaleApiController( // Process the sale when (val result = saleProcessingService.processSaleWithValidation(saleBean, items)) { is SaleResult.Success -> { - // Print receipt (staffBarcode is now nullable) + // Print receipt receiptService.printReceipt( saleBean.storeId, items, - saleBean.staffBarcode ?: "", saleBean.deposit, ) diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/StaffApiController.kt b/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/StaffApiController.kt deleted file mode 100644 index 5e32299..0000000 --- a/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/StaffApiController.kt +++ /dev/null @@ -1,75 +0,0 @@ -package info.nukoneko.kidspos.server.controller.api - -import info.nukoneko.kidspos.server.controller.dto.request.CreateStaffRequest -import info.nukoneko.kidspos.server.domain.exception.ResourceNotFoundException -import info.nukoneko.kidspos.server.entity.StaffEntity -import info.nukoneko.kidspos.server.service.StaffService -import jakarta.validation.Valid -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.* - -/** - * スタッフAPIコントローラー - * - * スタッフ情報の取得と管理を行うREST APIエンドポイントを提供 - */ -@RestController -@RequestMapping("/api/staff") -class StaffApiController { - @Autowired - private lateinit var service: StaffService - - @GetMapping("/{barcode}") - fun getStaff( - @PathVariable barcode: String, - ): ResponseEntity { - val staff = - service.findStaff(barcode) - ?: throw ResourceNotFoundException("Staff with barcode $barcode not found") - return ResponseEntity.ok(staff) - } - - @GetMapping - fun getAllStaff(): ResponseEntity> = ResponseEntity.ok(service.findAll()) - - @PostMapping - fun createStaff( - @Valid @RequestBody request: CreateStaffRequest, - ): ResponseEntity { - val staff = - StaffEntity( - barcode = request.barcode, - name = request.name, - ) - val savedStaff = service.save(staff) - return ResponseEntity.status(HttpStatus.CREATED).body(savedStaff) - } - - @PutMapping("/{barcode}") - fun updateStaff( - @PathVariable barcode: String, - @Valid @RequestBody request: CreateStaffRequest, - ): ResponseEntity { - val existingStaff = - service.findStaff(barcode) - ?: throw ResourceNotFoundException("Staff with barcode $barcode not found") - - val updatedStaff = existingStaff.copy(name = request.name) - val savedStaff = service.save(updatedStaff) - return ResponseEntity.ok(savedStaff) - } - - @DeleteMapping("/{barcode}") - fun deleteStaff( - @PathVariable barcode: String, - ): ResponseEntity { - val existingStaff = - service.findStaff(barcode) - ?: throw ResourceNotFoundException("Staff with barcode $barcode not found") - - service.delete(existingStaff) - return ResponseEntity.noContent().build() - } -} diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/UserApiController.kt b/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/UserApiController.kt deleted file mode 100644 index dc28417..0000000 --- a/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/UserApiController.kt +++ /dev/null @@ -1,87 +0,0 @@ -package info.nukoneko.kidspos.server.controller.api - -import info.nukoneko.kidspos.server.domain.exception.DuplicateResourceException -import info.nukoneko.kidspos.server.domain.exception.ResourceNotFoundException -import info.nukoneko.kidspos.server.entity.StaffEntity -import info.nukoneko.kidspos.server.service.StaffService -import jakarta.validation.Valid -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.* - -/** - * ユーザーAPIコントローラー - * - * ユーザー(スタッフ)情報の取得と管理を行うREST APIエンドポイントを提供 - */ -@RestController -@RequestMapping("/api/users") -class UserApiController { - @Autowired - private lateinit var staffService: StaffService - - @GetMapping - fun getUsers(): ResponseEntity> = ResponseEntity.ok(staffService.findAll()) - - @GetMapping("/{barcode}") - fun getUser( - @PathVariable barcode: String, - ): ResponseEntity { - val user = - staffService.findStaff(barcode) - ?: throw ResourceNotFoundException("User with barcode $barcode not found") - return ResponseEntity.ok(user) - } - - @PostMapping - fun createUser( - @Valid @RequestBody user: StaffEntity, - ): ResponseEntity { - // Validate required fields - if (user.barcode.isBlank()) { - throw IllegalArgumentException("Barcode is required") - } - if (user.name.isBlank()) { - throw IllegalArgumentException("Name is required") - } - - // Check for duplicate barcode - if (staffService.findStaff(user.barcode) != null) { - throw DuplicateResourceException("User with barcode ${user.barcode} already exists") - } - - val savedUser = staffService.save(user) - return ResponseEntity.status(HttpStatus.CREATED).body(savedUser) - } - - @PutMapping("/{barcode}") - fun updateUser( - @PathVariable barcode: String, - @Valid @RequestBody user: StaffEntity, - ): ResponseEntity { - // Check if user exists - staffService.findStaff(barcode) - ?: throw ResourceNotFoundException("User with barcode $barcode not found") - - // Validate required fields - if (user.name.isBlank()) { - throw IllegalArgumentException("Name is required") - } - - val updatedUser = staffService.save(user.copy(barcode = barcode)) - return ResponseEntity.ok(updatedUser) - } - - @DeleteMapping("/{barcode}") - fun deleteUser( - @PathVariable barcode: String, - ): ResponseEntity { - // Check if user exists - staffService.findStaff(barcode) - ?: throw ResourceNotFoundException("User with barcode $barcode not found") - - staffService.delete(barcode) - return ResponseEntity.noContent().build() - } -} diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/model/SaleBean.kt b/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/model/SaleBean.kt index edd1c0d..4af9f28 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/model/SaleBean.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/model/SaleBean.kt @@ -2,7 +2,6 @@ package info.nukoneko.kidspos.server.controller.api.model data class SaleBean( val storeId: Int, - val staffBarcode: String, val deposit: Int, val itemIds: String, ) diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/model/StaffBean.kt b/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/model/StaffBean.kt deleted file mode 100644 index 4a9ce60..0000000 --- a/src/main/kotlin/info/nukoneko/kidspos/server/controller/api/model/StaffBean.kt +++ /dev/null @@ -1,6 +0,0 @@ -package info.nukoneko.kidspos.server.controller.api.model - -data class StaffBean( - var barcode: String, - val name: String, -) diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/CreateItemRequest.kt b/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/CreateItemRequest.kt index 81c1446..a5bba20 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/CreateItemRequest.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/CreateItemRequest.kt @@ -15,9 +15,8 @@ data class CreateItemRequest( @field:NotBlank(message = "Item name is required") @field:Size(max = Constants.Validation.NAME_MAX_LENGTH) val name: String, - @field:NotBlank(message = "Barcode is required") @field:Pattern(regexp = Constants.Validation.BARCODE_PATTERN, message = "Invalid barcode format") - val barcode: String, + val barcode: String?, @field:Min(value = 0, message = "Price must be non-negative") val price: Int, ) diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/CreateSaleRequest.kt b/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/CreateSaleRequest.kt index 7ba393b..76f8595 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/CreateSaleRequest.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/CreateSaleRequest.kt @@ -12,7 +12,6 @@ import jakarta.validation.constraints.NotNull data class CreateSaleRequest( @field:NotNull(message = "Store ID is required") val storeId: Int, - val staffBarcode: String? = null, @field:NotBlank(message = "Item IDs are required") val itemIds: String = "", @field:Min(value = 0, message = "Deposit must be non-negative") diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/CreateStaffRequest.kt b/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/CreateStaffRequest.kt deleted file mode 100644 index 4f2a9d7..0000000 --- a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/CreateStaffRequest.kt +++ /dev/null @@ -1,21 +0,0 @@ -package info.nukoneko.kidspos.server.controller.dto.request - -import info.nukoneko.kidspos.common.Constants -import jakarta.validation.constraints.* - -/** - * スタッフ作成リクエストDTO - * - * 新規スタッフ作成時のリクエストデータを表現 - */ -data class CreateStaffRequest( - @field:NotBlank(message = "Staff name is required") - @field:Size(max = Constants.Validation.NAME_MAX_LENGTH, message = "Staff name is too long") - val name: String, - @field:NotBlank(message = "Barcode is required") - @field:Pattern(regexp = Constants.Validation.BARCODE_PATTERN, message = "Invalid barcode format") - val barcode: String, - @field:NotNull(message = "Store ID is required") - @field:Min(value = 1, message = "Store ID must be positive") - val storeId: Int, -) diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/ItemBean.kt b/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/ItemBean.kt index 76789ea..2f4ebbe 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/ItemBean.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/ItemBean.kt @@ -2,7 +2,7 @@ package info.nukoneko.kidspos.server.controller.dto.request data class ItemBean( val id: Int? = null, - val barcode: String, + val barcode: String?, val name: String, val price: Int, ) diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/SaleBean.kt b/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/SaleBean.kt index 5064b95..bb2d0ff 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/SaleBean.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/SaleBean.kt @@ -2,7 +2,6 @@ package info.nukoneko.kidspos.server.controller.dto.request data class SaleBean( val storeId: Int, - val staffBarcode: String? = null, val deposit: Int, val itemIds: String, ) diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/StaffBean.kt b/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/StaffBean.kt deleted file mode 100644 index b310674..0000000 --- a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/request/StaffBean.kt +++ /dev/null @@ -1,6 +0,0 @@ -package info.nukoneko.kidspos.server.controller.dto.request - -data class StaffBean( - var barcode: String, - val name: String, -) diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/response/SaleReportData.kt b/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/response/SaleReportData.kt index 792b3f2..c529082 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/response/SaleReportData.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/response/SaleReportData.kt @@ -6,8 +6,6 @@ data class SaleReportData( val saleId: Int, val storeId: Int, val storeName: String, - val staffId: Int, - val staffName: String, val quantity: Int, val amount: Int, val createdAt: Date, diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/response/SaleResponse.kt b/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/response/SaleResponse.kt index 55b4725..9809ea2 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/response/SaleResponse.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/response/SaleResponse.kt @@ -13,8 +13,6 @@ data class SaleResponse( val id: Int, val storeId: Int, val storeName: String, - val staffId: String, - val staffName: String, val totalAmount: Int, val deposit: Int, val change: Int, diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/response/StaffResponse.kt b/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/response/StaffResponse.kt deleted file mode 100644 index 61d52e1..0000000 --- a/src/main/kotlin/info/nukoneko/kidspos/server/controller/dto/response/StaffResponse.kt +++ /dev/null @@ -1,21 +0,0 @@ -package info.nukoneko.kidspos.server.controller.dto.response - -import java.time.LocalDateTime - -/** - * スタッフレスポンスDTO - * - * スタッフ情報のAPIレスポンスデータを表現 - */ -data class StaffResponse( - val id: String, - val name: String, - val barcode: String, - val storeId: Int, - val storeName: String? = null, - val createdAt: LocalDateTime? = null, - val updatedAt: LocalDateTime? = null, -) { - val displayName: String - get() = "$name (ID: $id)" -} diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/controller/front/StaffsController.kt b/src/main/kotlin/info/nukoneko/kidspos/server/controller/front/StaffsController.kt deleted file mode 100644 index fca35b1..0000000 --- a/src/main/kotlin/info/nukoneko/kidspos/server/controller/front/StaffsController.kt +++ /dev/null @@ -1,22 +0,0 @@ -package info.nukoneko.kidspos.server.controller.front - -import info.nukoneko.kidspos.server.service.StaffService -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.stereotype.Controller -import org.springframework.ui.Model -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.RequestMapping - -@Controller -@RequestMapping("/staffs") -class StaffsController { - @Autowired - private lateinit var staffService: StaffService - - @GetMapping - fun index(model: Model): String { - model.addAttribute("title", javaClass.simpleName) - model.addAttribute("data", staffService.findAll()) - return "staffs/index" - } -} diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/entity/SaleEntity.kt b/src/main/kotlin/info/nukoneko/kidspos/server/entity/SaleEntity.kt index 424f403..19cb4e7 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/entity/SaleEntity.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/entity/SaleEntity.kt @@ -11,7 +11,6 @@ import java.util.* * 販売取引情報を表現するデータベースエンティティ * @property id 売上げID * @property storeId 店舗ID - * @property staffId スタッフID * @property quantity 数量 * @property amount 売上げ金額 * @property deposit 預かり金 @@ -22,7 +21,6 @@ import java.util.* data class SaleEntity( @Id var id: Int = 0, // 売り上げID val storeId: Int, // 店舗ID - val staffId: Int, // スタッフID val quantity: Int, // 数量 val amount: Int, // 売り上げ val deposit: Int, diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/entity/StaffEntity.kt b/src/main/kotlin/info/nukoneko/kidspos/server/entity/StaffEntity.kt deleted file mode 100644 index 874197b..0000000 --- a/src/main/kotlin/info/nukoneko/kidspos/server/entity/StaffEntity.kt +++ /dev/null @@ -1,23 +0,0 @@ -package info.nukoneko.kidspos.server.entity - -import jakarta.persistence.Entity -import jakarta.persistence.Id -import jakarta.persistence.Table -import jakarta.validation.constraints.NotBlank - -/** - * スタッフエンティティ - * - * 店舗スタッフ情報を表現するデータベースエンティティ - * @property barcode スタッフのバーコードID - * @property name スタッフ名 - */ -@Entity -@Table(name = "staff") -data class StaffEntity( - @Id - @field:NotBlank(message = "Barcode is required") - var barcode: String, - @field:NotBlank(message = "Name is required") - val name: String, -) diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/repository/StaffRepository.kt b/src/main/kotlin/info/nukoneko/kidspos/server/repository/StaffRepository.kt deleted file mode 100644 index 48d1472..0000000 --- a/src/main/kotlin/info/nukoneko/kidspos/server/repository/StaffRepository.kt +++ /dev/null @@ -1,13 +0,0 @@ -package info.nukoneko.kidspos.server.repository - -import info.nukoneko.kidspos.server.entity.StaffEntity -import org.springframework.data.jpa.repository.JpaRepository -import org.springframework.stereotype.Repository - -/** - * スタッフエンティティのリポジトリインターフェース - * - * スタッフ情報のCRUD操作を提供 - */ -@Repository -interface StaffRepository : JpaRepository diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/service/ItemService.kt b/src/main/kotlin/info/nukoneko/kidspos/server/service/ItemService.kt index 627c6cd..24c61c1 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/service/ItemService.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/service/ItemService.kt @@ -1,5 +1,6 @@ package info.nukoneko.kidspos.server.service +import info.nukoneko.kidspos.common.Constants import info.nukoneko.kidspos.common.service.IdGenerationService import info.nukoneko.kidspos.server.config.CacheConfig import info.nukoneko.kidspos.server.controller.dto.request.ItemBean @@ -81,9 +82,19 @@ class ItemService( } else { idGenerationService.generateNextId(repository) } - val item = ItemEntity(generatedId, itemBean.barcode, itemBean.name, itemBean.price) + + // バーコードが空の場合は自動生成(A + 種別コード01 + ID(6桁) + A) + val finalBarcode = + if (itemBean.barcode.isNullOrBlank()) { + val idPadded = generatedId.toString().padStart(Constants.Barcode.ID_LENGTH, '0') + "${Constants.Barcode.PREFIX}${Constants.Barcode.TYPE_ITEM}$idPadded${Constants.Barcode.SUFFIX}" + } else { + itemBean.barcode + } + + val item = ItemEntity(generatedId, finalBarcode, itemBean.name, itemBean.price) val savedItem = repository.save(item) - logger.info("Item created successfully with ID: {}", savedItem.id) + logger.info("Item created successfully with ID: {}, barcode: {}", savedItem.id, savedItem.barcode) return savedItem } diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/service/ReceiptService.kt b/src/main/kotlin/info/nukoneko/kidspos/server/service/ReceiptService.kt index 580b929..541ca40 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/service/ReceiptService.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/service/ReceiptService.kt @@ -17,7 +17,6 @@ import java.util.* @Service class ReceiptService( private val storeService: StoreService, - private val staffService: StaffService, private val appProperties: AppProperties, ) { private val logger = LoggerFactory.getLogger(ReceiptService::class.java) @@ -28,13 +27,12 @@ class ReceiptService( fun printReceipt( storeId: Int, items: List, - staffBarcode: String, deposit: Int, ): Boolean { logger.debug("Printing receipt for store: {}, items: {}", storeId, items.size) return try { - val receiptDetail = createReceiptDetail(storeId, items, staffBarcode, deposit) + val receiptDetail = createReceiptDetail(storeId, items, deposit) val printerIp = getPrinterIp(storeId) ?: return false sendToPrinter(printerIp, receiptDetail) @@ -52,26 +50,23 @@ class ReceiptService( private fun createReceiptDetail( storeId: Int, items: List, - staffBarcode: String, deposit: Int, ): ReceiptDetail { val itemEntities = items.map { itemBean -> ItemEntity( id = itemBean.id!!, - barcode = itemBean.barcode, + barcode = itemBean.barcode ?: "", name = itemBean.name, price = itemBean.price, ) } val storeName = storeService.findStore(storeId)?.name - val staffName = staffService.findStaff(staffBarcode)?.name return ReceiptDetail( items = itemEntities, storeName = storeName, - staffName = staffName, deposit = deposit, transactionId = UUID.randomUUID().toString(), createdAt = Date(), @@ -126,18 +121,15 @@ class ReceiptService( fun generateReceiptContent( storeId: Int, items: List, - staffBarcode: String, deposit: Int, ): String { val storeName = storeService.findStore(storeId)?.name ?: "Unknown Store" - val staffName = staffService.findStaff(staffBarcode)?.name ?: "Unknown Staff" val totalAmount = items.sumOf { it.price } val change = deposit - totalAmount return buildString { appendLine("========== RECEIPT ==========") appendLine("Store: $storeName") - appendLine("Staff: $staffName") appendLine("Date: ${Date()}") appendLine("-----------------------------") items.forEach { item -> diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/service/SaleExcelReportService.kt b/src/main/kotlin/info/nukoneko/kidspos/server/service/SaleExcelReportService.kt index 9f8f4e3..adff01b 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/service/SaleExcelReportService.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/service/SaleExcelReportService.kt @@ -24,7 +24,6 @@ class SaleExcelReportService( private val saleDetailRepository: SaleDetailRepository, private val itemRepository: ItemRepository, private val storeRepository: StoreRepository, - private val staffRepository: StaffRepository, ) { private val logger = LoggerFactory.getLogger(SaleExcelReportService::class.java) private val dateFormat = SimpleDateFormat("yyyy/MM/dd HH:mm") @@ -63,12 +62,6 @@ class SaleExcelReportService( private fun prepareSalesReportData(sales: List): List = sales.map { sale -> val store = storeRepository.findById(sale.storeId).orElse(null) - val staff = - if (sale.staffId > 0) { - staffRepository.findById(sale.staffId.toString()).orElse(null) - } else { - null - } val details = saleDetailRepository.findBySaleId(sale.id).map { detail -> val item = itemRepository.findById(detail.itemId).orElse(null) @@ -85,8 +78,6 @@ class SaleExcelReportService( saleId = sale.id, storeId = sale.storeId, storeName = store?.name ?: "不明な店舗", - staffId = sale.staffId, - staffName = staff?.name ?: "不明なスタッフ", quantity = sale.quantity, amount = sale.amount, createdAt = sale.createdAt, @@ -224,7 +215,7 @@ class SaleExcelReportService( // ヘッダー行 val headerRow = sheet.createRow(rowNum++) - val headers = listOf("売上ID", "日時", "店舗名", "スタッフ名", "商品数", "金額", "商品明細") + val headers = listOf("売上ID", "日時", "店舗名", "商品数", "金額", "商品明細") headers.forEachIndexed { index, header -> val cell = headerRow.createCell(index) cell.setCellValue(header) @@ -241,10 +232,9 @@ class SaleExcelReportService( dateCell.cellStyle = dateStyle row.createCell(2).setCellValue(sale.storeName) - row.createCell(3).setCellValue(sale.staffName) - row.createCell(4).setCellValue(sale.quantity.toDouble()) + row.createCell(3).setCellValue(sale.quantity.toDouble()) - val amountCell = row.createCell(5) + val amountCell = row.createCell(4) amountCell.setCellValue(sale.amount.toDouble()) amountCell.cellStyle = currencyStyle @@ -256,18 +246,18 @@ class SaleExcelReportService( } else { "-" } - row.createCell(6).setCellValue(detailText) + row.createCell(5).setCellValue(detailText) } // 合計行 val totalRow = sheet.createRow(rowNum++) - totalRow.createCell(3).setCellValue("合計") - val totalCell = totalRow.createCell(5) + totalRow.createCell(2).setCellValue("合計") + val totalCell = totalRow.createCell(4) totalCell.setCellValue(reportData.sumOf { it.amount }.toDouble()) totalCell.cellStyle = currencyStyle // 列幅の自動調整 - for (i in 0..6) { + for (i in 0..5) { sheet.autoSizeColumn(i) } } diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/service/SalePersistenceService.kt b/src/main/kotlin/info/nukoneko/kidspos/server/service/SalePersistenceService.kt index 6285ae3..96adf97 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/service/SalePersistenceService.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/service/SalePersistenceService.kt @@ -34,7 +34,6 @@ class SalePersistenceService( items: List, ): SaleEntity { val saleId = idGenerationService.generateNextId(saleRepository) - val staffId = extractStaffId(saleBean.staffBarcode) val totalAmount = saleCalculationService.calculateSaleAmount(items) val quantity = saleCalculationService.calculateQuantity(items) @@ -42,7 +41,6 @@ class SalePersistenceService( SaleEntity( id = saleId, storeId = saleBean.storeId, - staffId = staffId, quantity = quantity, amount = totalAmount, deposit = saleBean.deposit, @@ -99,16 +97,6 @@ class SalePersistenceService( return savedDetails } - /** - * Extract staff ID from barcode - */ - private fun extractStaffId(staffBarcode: String?): Int = - if (!staffBarcode.isNullOrEmpty() && staffBarcode.length > 4) { - staffBarcode.takeLast(3).toIntOrNull() ?: 0 - } else { - 0 - } - /** * Find sale by ID */ diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/service/SaleReportService.kt b/src/main/kotlin/info/nukoneko/kidspos/server/service/SaleReportService.kt index 91246e0..f0624c2 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/service/SaleReportService.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/service/SaleReportService.kt @@ -31,7 +31,6 @@ class SaleReportService( private val saleDetailRepository: SaleDetailRepository, private val itemRepository: ItemRepository, private val storeRepository: StoreRepository, - private val staffRepository: StaffRepository, ) { private val logger = LoggerFactory.getLogger(SaleReportService::class.java) private val dateFormat = SimpleDateFormat("yyyy/MM/dd HH:mm") @@ -70,12 +69,6 @@ class SaleReportService( private fun prepareSalesReportData(sales: List): List = sales.map { sale -> val store = storeRepository.findById(sale.storeId).orElse(null) - val staff = - if (sale.staffId > 0) { - staffRepository.findById(sale.staffId.toString()).orElse(null) - } else { - null - } val details = saleDetailRepository.findBySaleId(sale.id).map { detail -> val item = itemRepository.findById(detail.itemId).orElse(null) @@ -92,8 +85,6 @@ class SaleReportService( saleId = sale.id, storeId = sale.storeId, storeName = store?.name ?: "不明な店舗", - staffId = sale.staffId, - staffName = staff?.name ?: "不明なスタッフ", quantity = sale.quantity, amount = sale.amount, createdAt = sale.createdAt, @@ -209,14 +200,13 @@ class SaleReportService( document.add(tableTitle) val table = - Table(UnitValue.createPercentArray(floatArrayOf(10f, 20f, 15f, 15f, 15f, 15f, 10f))) + Table(UnitValue.createPercentArray(floatArrayOf(10f, 20f, 20f, 15f, 15f, 20f))) .useAllAvailableWidth() // ヘッダー行 table.addHeaderCell(createHeaderCell("売上ID")) table.addHeaderCell(createHeaderCell("日時")) table.addHeaderCell(createHeaderCell("店舗")) - table.addHeaderCell(createHeaderCell("スタッフ")) table.addHeaderCell(createHeaderCell("商品数")) table.addHeaderCell(createHeaderCell("金額")) table.addHeaderCell(createHeaderCell("詳細")) @@ -226,7 +216,6 @@ class SaleReportService( table.addCell(createDataCell(sale.saleId.toString())) table.addCell(createDataCell(dateFormat.format(sale.createdAt))) table.addCell(createDataCell(sale.storeName)) - table.addCell(createDataCell(sale.staffName)) table.addCell(createDataCell(sale.quantity.toString())) table.addCell(createDataCell("¥${numberFormat.format(sale.amount)}")) diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/service/SaleService.kt b/src/main/kotlin/info/nukoneko/kidspos/server/service/SaleService.kt index 775f0ca..00b837c 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/service/SaleService.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/service/SaleService.kt @@ -39,9 +39,6 @@ class SaleService( @Autowired private lateinit var storeRepository: StoreRepository - @Autowired - private lateinit var staffRepository: StaffRepository - fun findAllSale(): List = saleRepository.findAll() fun findAllSaleDetail(): List = saleDetailRepository.findAll() @@ -64,14 +61,6 @@ class SaleService( val id = idGenerationService.generateNextId(saleRepository) // 売り上げを保存 - val staffId = - if (!saleBean.staffBarcode.isNullOrEmpty() && saleBean.staffBarcode.length > Constants.Barcode.MIN_LENGTH) { - saleBean.staffBarcode - .substring(saleBean.staffBarcode.length - Constants.Barcode.SUFFIX_LENGTH) - .toIntOrNull() ?: 0 - } else { - 0 - } items.forEach { logger.debug("Item - ID: {}, Name: {}, Price: {}", it.id, it.name, it.price) } @@ -79,7 +68,6 @@ class SaleService( SaleEntity( id, saleBean.storeId, - staffId, items.size, items.sumOf { it.price }, saleBean.deposit, diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/service/SaleValidationService.kt b/src/main/kotlin/info/nukoneko/kidspos/server/service/SaleValidationService.kt index 1a17bfe..658b3c3 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/service/SaleValidationService.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/service/SaleValidationService.kt @@ -33,8 +33,6 @@ class SaleValidationService { logger.debug("Validating sale request for store: {}", saleBean.storeId) validateStoreId(saleBean.storeId) - // staffBarcode is now optional, skip validation - // validateStaffBarcode(saleBean.staffBarcode) validateItems(items) validateDeposit(saleBean, items) @@ -50,15 +48,6 @@ class SaleValidationService { } } - /** - * Validate staff barcode - */ - private fun validateStaffBarcode(staffBarcode: String) { - if (staffBarcode.isBlank()) { - throw IllegalArgumentException("Staff barcode cannot be empty") - } - } - /** * Validate items list */ diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/service/StaffService.kt b/src/main/kotlin/info/nukoneko/kidspos/server/service/StaffService.kt deleted file mode 100644 index 8729c2c..0000000 --- a/src/main/kotlin/info/nukoneko/kidspos/server/service/StaffService.kt +++ /dev/null @@ -1,67 +0,0 @@ -package info.nukoneko.kidspos.server.service - -import info.nukoneko.kidspos.server.config.CacheConfig -import info.nukoneko.kidspos.server.entity.StaffEntity -import info.nukoneko.kidspos.server.repository.StaffRepository -import org.slf4j.LoggerFactory -import org.springframework.cache.annotation.Cacheable -import org.springframework.data.repository.findByIdOrNull -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional - -/** - * Service for managing staff member operations - * - * Handles CRUD operations for staff data with caching support for improved - * performance. Staff members are identified by barcode IDs and this service - * provides efficient lookup capabilities for POS operations where staff - * authentication is required. - * - * Key responsibilities: - * - Managing staff member data retrieval - * - Providing cached access to staff information - * - Supporting barcode-based staff identification - * - Maintaining data consistency through proper caching strategies - * - * Caching strategy: - * - All staff data cached for bulk operations - * - Individual staff lookup cached by barcode ID - * - Cache warming through findAll() method - * - * @constructor Creates StaffService with required repository - * @param repository Repository for staff data access - */ -@Service -@Transactional -class StaffService( - private val repository: StaffRepository, -) { - private val logger = LoggerFactory.getLogger(StaffService::class.java) - - @Cacheable(value = [CacheConfig.STAFF_CACHE]) - fun findAll(): List { - logger.debug("Fetching all staff from database") - return repository.findAll() - } - - @Cacheable(value = [CacheConfig.STAFF_BY_ID_CACHE], key = "#barcode") - fun findStaff(barcode: String): StaffEntity? { - logger.debug("Fetching staff by barcode: {} from database", barcode) - return repository.findByIdOrNull(barcode) - } - - fun save(staff: StaffEntity): StaffEntity { - logger.debug("Saving staff with barcode: {}", staff.barcode) - return repository.save(staff) - } - - fun delete(barcode: String) { - logger.debug("Deleting staff with barcode: {}", barcode) - repository.deleteById(barcode) - } - - fun delete(staff: StaffEntity) { - logger.debug("Deleting staff entity with barcode: {}", staff.barcode) - repository.delete(staff) - } -} diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/service/ValidationService.kt b/src/main/kotlin/info/nukoneko/kidspos/server/service/ValidationService.kt index f689c32..52ab049 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/service/ValidationService.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/service/ValidationService.kt @@ -3,7 +3,6 @@ package info.nukoneko.kidspos.server.service import info.nukoneko.kidspos.common.Constants import info.nukoneko.kidspos.server.domain.exception.ValidationException import info.nukoneko.kidspos.server.repository.ItemRepository -import info.nukoneko.kidspos.server.repository.StaffRepository import info.nukoneko.kidspos.server.repository.StoreRepository import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @@ -24,21 +23,19 @@ import org.springframework.stereotype.Service * - Centralizing business rule logic for maintainability * * Validation categories: - * - Entity existence validation (items, stores, staff) - * - Uniqueness validation (barcodes, staff IDs) + * - Entity existence validation (items, stores) + * - Uniqueness validation (barcodes) * - Range validation (prices, quantities) * - Business rule enforcement * * @constructor Creates ValidationService with required repositories * @param itemRepository Repository for item data validation * @param storeRepository Repository for store data validation - * @param staffRepository Repository for staff data validation */ @Service class ValidationService( private val itemRepository: ItemRepository, private val storeRepository: StoreRepository, - private val staffRepository: StaffRepository, ) { private val logger = LoggerFactory.getLogger(ValidationService::class.java) @@ -56,17 +53,15 @@ class ValidationService( } } - fun validateStaffExists(staffId: String) { - if (!staffRepository.existsById(staffId)) { - logger.warn("Validation failed: Staff with ID {} does not exist", staffId) - throw ValidationException("Staff with ID $staffId does not exist") - } - } - fun validateBarcodeUnique( - barcode: String, + barcode: String?, excludeId: Int? = null, ) { + // nullの場合は自動生成されるためバリデーションスキップ + if (barcode.isNullOrBlank()) { + return + } + val existingItem = itemRepository.findByBarcode(barcode) if (existingItem != null && existingItem.id != excludeId) { logger.warn("Validation failed: Barcode {} already exists", barcode) @@ -74,20 +69,6 @@ class ValidationService( } } - fun validateStaffBarcodeUnique( - barcode: String, - excludeId: String? = null, - ) { - // Since StaffRepository doesn't have findByBarcode, we'll check by ID for now - // This would need to be enhanced with a proper query method - if (excludeId == null || excludeId != barcode) { - if (staffRepository.existsById(barcode)) { - logger.warn("Validation failed: Staff barcode {} already exists", barcode) - throw ValidationException("Staff barcode $barcode already exists") - } - } - } - fun validateStoreBarcodeUnique( barcode: String, excludeId: Int? = null, diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/service/mapper/ItemMapper.kt b/src/main/kotlin/info/nukoneko/kidspos/server/service/mapper/ItemMapper.kt index 7604d4b..a2f48ff 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/service/mapper/ItemMapper.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/service/mapper/ItemMapper.kt @@ -45,7 +45,7 @@ class ItemMapper { ): ItemEntity = ItemEntity( id = id, - barcode = request.barcode, + barcode = request.barcode ?: "", name = request.name, price = request.price, ) diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/service/mapper/SaleMapper.kt b/src/main/kotlin/info/nukoneko/kidspos/server/service/mapper/SaleMapper.kt index 2ffea3f..ee8e45e 100644 --- a/src/main/kotlin/info/nukoneko/kidspos/server/service/mapper/SaleMapper.kt +++ b/src/main/kotlin/info/nukoneko/kidspos/server/service/mapper/SaleMapper.kt @@ -5,7 +5,6 @@ import info.nukoneko.kidspos.server.controller.dto.response.SaleResponse import info.nukoneko.kidspos.server.entity.SaleEntity import info.nukoneko.kidspos.server.repository.ItemRepository import info.nukoneko.kidspos.server.repository.SaleDetailRepository -import info.nukoneko.kidspos.server.repository.StaffRepository import info.nukoneko.kidspos.server.repository.StoreRepository import org.springframework.stereotype.Component import java.time.LocalDateTime @@ -24,8 +23,7 @@ import java.time.ZoneId * with full relationship resolution and calculated fields * - **Relationship Resolution**: Dynamically resolves and populates related data from: * - Store information via StoreRepository - * - Staff details via StaffRepository - * - Item details via ItemRepository + * * - Item details via ItemRepository * - Sale line items via SaleDetailRepository * - **Financial Calculations**: Computes derived values like change amounts and subtotals * - **Temporal Conversion**: Handles timestamp conversions from database format to LocalDateTime @@ -35,7 +33,6 @@ import java.time.ZoneId * ``` * SaleEntity -> SaleResponse (with full relationship data) * ├─ Store lookup -> StoreName population - * ├─ Staff lookup -> StaffName population * ├─ SaleDetail lookup -> Item list construction * └─ Item lookup per detail -> Complete item information * @@ -46,7 +43,7 @@ import java.time.ZoneId * - **Multi-Repository Coordination**: Performs coordinated lookups across four repositories * to build complete sale representations with all related data * - **Graceful Degradation**: Handles missing related entities by providing fallback values - * ("Unknown Store", "Unknown Staff", "Unknown" item names) + * ("Unknown Store", "Unknown" item names) * - **Financial Integrity**: Calculates change amounts using deposit minus total logic * - **Timezone Handling**: Converts UTC timestamps to system timezone for display * - **Performance Considerations**: Each sale response requires multiple database queries, @@ -61,12 +58,10 @@ import java.time.ZoneId * ## Repository Dependencies: * This mapper requires injected repositories for relationship resolution: * - StoreRepository: Store name and details - * - StaffRepository: Staff member information * - ItemRepository: Product details and pricing * - SaleDetailRepository: Individual line items per sale * * @param storeRepository Repository for store data lookup - * @param staffRepository Repository for staff data lookup * @param itemRepository Repository for item/product data lookup * @param saleDetailRepository Repository for sale line item lookup * @@ -78,13 +73,11 @@ import java.time.ZoneId @Component class SaleMapper( private val storeRepository: StoreRepository, - private val staffRepository: StaffRepository, private val itemRepository: ItemRepository, private val saleDetailRepository: SaleDetailRepository, ) { fun toResponse(entity: SaleEntity): SaleResponse { val store = storeRepository.findById(entity.storeId).orElse(null) - val staff = staffRepository.findById(entity.staffId.toString()).orElse(null) val saleDetails = saleDetailRepository.findBySaleId(entity.id) val items = @@ -104,8 +97,6 @@ class SaleMapper( id = entity.id, storeId = entity.storeId, storeName = store?.name ?: "Unknown Store", - staffId = entity.staffId.toString(), - staffName = staff?.name ?: "Unknown Staff", totalAmount = entity.amount, deposit = entity.deposit, change = entity.deposit - entity.amount, diff --git a/src/main/kotlin/info/nukoneko/kidspos/server/service/mapper/StaffMapper.kt b/src/main/kotlin/info/nukoneko/kidspos/server/service/mapper/StaffMapper.kt deleted file mode 100644 index f3d4ca7..0000000 --- a/src/main/kotlin/info/nukoneko/kidspos/server/service/mapper/StaffMapper.kt +++ /dev/null @@ -1,63 +0,0 @@ -package info.nukoneko.kidspos.server.service.mapper - -import info.nukoneko.kidspos.server.controller.dto.response.StaffResponse -import info.nukoneko.kidspos.server.entity.StaffEntity -import org.springframework.stereotype.Component - -/** - * Data mapper for Staff entities and DTOs - * - * This mapper handles unidirectional data transformation from Staff domain objects - * to their corresponding Data Transfer Objects for API responses. It manages - * the conversion of staff information while handling data structure differences - * and providing default values for missing attributes. - * - * ## Mapping Responsibilities: - * - **Entity to Response**: Transforms StaffEntity objects into StaffResponse DTOs - * for API consumption and frontend display - * - **ID Normalization**: Uses barcode as the primary identifier in both ID and - * barcode fields of the response to maintain consistency - * - **Default Value Assignment**: Provides sensible defaults for missing data - * such as default store assignments and null timestamps - * - **Batch Operations**: Supports efficient list transformations for staff collections - * - * ## Data Flow Patterns: - * ``` - * StaffEntity -> StaffResponse (for API responses) - * List -> List (for collection endpoints) - * ``` - * - * ## Implementation Specifics: - * - **Barcode as ID**: The StaffEntity uses barcode as the primary identifier, - * which is mapped to both the ID and barcode fields in the response - * - **Default Store Assignment**: Staff members are assigned to store ID 1 by default - * since store relationships are not maintained in the current entity structure - * - **Missing Relationships**: Store name lookup is not performed, leaving storeName null - * - **Timestamp Compatibility**: CreatedAt and updatedAt fields are set to null - * as they're not available in the current entity - * - * ## Business Context: - * Staff members represent employees who can operate the Kids POS system. - * This mapper ensures consistent staff data presentation while maintaining - * compatibility with response contracts that expect store associations and - * audit timestamps, even when this data is not currently tracked. - * - * @see StaffEntity - * @see StaffResponse - * @since 1.0.0 - */ -@Component -class StaffMapper { - fun toResponse(entity: StaffEntity): StaffResponse = - StaffResponse( - id = entity.barcode, // barcode is the ID in StaffEntity - name = entity.name, - barcode = entity.barcode, - storeId = 1, // Default store ID since not available in entity - storeName = null, // Not available without additional lookup - createdAt = null, // Not available in current entity - updatedAt = null, // Not available in current entity - ) - - fun toResponseList(entities: List): List = entities.map { toResponse(it) } -} diff --git a/src/main/resources/db/migration/V000__create_tables.sql b/src/main/resources/db/migration/V000__create_tables.sql index 07589d0..3d72c29 100644 --- a/src/main/resources/db/migration/V000__create_tables.sql +++ b/src/main/resources/db/migration/V000__create_tables.sql @@ -14,21 +14,6 @@ CREATE TABLE IF NOT EXISTS store printer_uri VARCHAR(255) ); --- Staff table -CREATE TABLE IF NOT EXISTS staff -( - id - INTEGER - PRIMARY - KEY - AUTOINCREMENT, - barcode - VARCHAR(255) NOT NULL UNIQUE, - name VARCHAR(255) NOT NULL, - discount DECIMAL(5, - 2) DEFAULT 0, - password VARCHAR(255) -); -- Item table CREATE TABLE IF NOT EXISTS item @@ -55,8 +40,6 @@ CREATE TABLE IF NOT EXISTS sale AUTOINCREMENT, store_id INTEGER, - staff_id - INTEGER, amount INTEGER NOT @@ -78,13 +61,6 @@ CREATE TABLE IF NOT EXISTS sale ( store_id ) REFERENCES store - ( - id - ), - FOREIGN KEY - ( - staff_id - ) REFERENCES staff ( id ) @@ -150,4 +126,4 @@ VALUES ('receipt.shop_name', 'KidsPOS Shop'); INSERT OR IGNORE INTO setting (key, value) -VALUES ('receipt.footer_message', 'Thank you for your purchase!'); \ No newline at end of file +VALUES ('receipt.footer_message', 'Thank you for your purchase!'); diff --git a/src/main/resources/db/migration/V001__create_indexes.sql b/src/main/resources/db/migration/V001__create_indexes.sql index 46a596d..73ebd9d 100644 --- a/src/main/resources/db/migration/V001__create_indexes.sql +++ b/src/main/resources/db/migration/V001__create_indexes.sql @@ -17,8 +17,6 @@ CREATE INDEX IF NOT EXISTS idx_sale_created_at ON sale (created_at); CREATE INDEX IF NOT EXISTS idx_sale_detail_sale_id ON sale_detail (sale_id); CREATE INDEX IF NOT EXISTS idx_sale_detail_item_id ON sale_detail (item_id); --- Index on staff barcode for authentication -CREATE INDEX IF NOT EXISTS idx_staff_barcode ON staff (barcode); -- Index on setting key for configuration lookups -CREATE INDEX IF NOT EXISTS idx_setting_key ON setting (key); \ No newline at end of file +CREATE INDEX IF NOT EXISTS idx_setting_key ON setting (key); diff --git a/src/main/resources/static/css/modern-style.css b/src/main/resources/static/css/modern-style.css index fb2f46b..c756164 100644 --- a/src/main/resources/static/css/modern-style.css +++ b/src/main/resources/static/css/modern-style.css @@ -181,11 +181,6 @@ body { padding: 1.5rem 2rem; } -/* Fix for duplicate elements */ -.card-modern .d-flex:nth-child(2) { - display: none !important; -} - /* Override old styles */ body { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; diff --git a/src/main/resources/tables.schema b/src/main/resources/tables.schema index c0a53e9..2d02857 100644 --- a/src/main/resources/tables.schema +++ b/src/main/resources/tables.schema @@ -9,7 +9,6 @@ CREATE TABLE IF NOT EXISTS sale ( id INTEGER PRIMARY KEY, store_id INTEGER, - staff_id INTEGER, quantity INTEGER, amount INTEGER, createdAt DATETIME DEFAULT CURRENT_TIMESTAMP @@ -30,12 +29,6 @@ CREATE TABLE IF NOT EXISTS store name varchar(255) NOT NULL ); -CREATE TABLE IF NOT EXISTS staff -( - id INTEGER PRIMARY KEY, - barcode varchar(255) NOT NULL UNIQUE, - name varchar(255) NOT NULL -); CREATE TABLE IF NOT EXISTS setting ( diff --git a/src/main/resources/templates/index-modern.html b/src/main/resources/templates/index-modern.html index d412a27..f84d3ed 100644 --- a/src/main/resources/templates/index-modern.html +++ b/src/main/resources/templates/index-modern.html @@ -47,13 +47,6 @@

-
-
-
0
-
スタッフ数
- -
-
@@ -160,4 +153,4 @@

}); - \ No newline at end of file + diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 19572a9..2648a25 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -81,17 +81,6 @@

店舗管理

- - \ No newline at end of file + diff --git a/src/main/resources/templates/items/edit.html b/src/main/resources/templates/items/edit.html index 53430b9..d393352 100644 --- a/src/main/resources/templates/items/edit.html +++ b/src/main/resources/templates/items/edit.html @@ -35,12 +35,18 @@

- - -
バーコードは8〜13桁の数字で入力してください
+ +
+ A01 + + A +
+ +
6桁の数字を入力(例: 000001 → A01000001A)
@@ -84,5 +90,37 @@

+ \ No newline at end of file diff --git a/src/main/resources/templates/items/index.html b/src/main/resources/templates/items/index.html index a8bd508..f2d2afc 100644 --- a/src/main/resources/templates/items/index.html +++ b/src/main/resources/templates/items/index.html @@ -26,10 +26,10 @@
@@ -161,6 +161,36 @@

printButton.disabled = !anyChecked; } + // PDF出力ボタン + const downloadPdfButton = document.getElementById('downloadPdf'); + downloadPdfButton.addEventListener('click', function () { + fetch('/api/item/barcode-pdf') + .then(response => { + if (!response.ok) { + throw new Error('PDF生成に失敗しました'); + } + return response.blob(); + }) + .then(blob => { + // PDFをダウンロード + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'barcodes.pdf'; + document.body.appendChild(a); + a.click(); + window.URL.revokeObjectURL(url); + document.body.removeChild(a); + }) + .catch(error => { + if (window.Swal) { + Swal.fire('エラー', error.message, 'error'); + } else { + alert('エラー: ' + error.message); + } + }); + }); + // 選択した商品を印刷 printButton.addEventListener('click', function () { const selectedIds = []; diff --git a/src/main/resources/templates/items/new.html b/src/main/resources/templates/items/new.html index d4c10bd..9ce0d28 100644 --- a/src/main/resources/templates/items/new.html +++ b/src/main/resources/templates/items/new.html @@ -40,12 +40,18 @@

- - -
バーコードは8〜13桁の数字で入力してください(任意)
+ +
+ A01 + + A +
+ +
6桁の数字を入力(空欄で自動生成、例: 000001 → A01000001A)
@@ -73,5 +79,26 @@

+ \ No newline at end of file diff --git a/src/main/resources/templates/layout-modern.html b/src/main/resources/templates/layout-modern.html index 9bb05ff..7e2436a 100644 --- a/src/main/resources/templates/layout-modern.html +++ b/src/main/resources/templates/layout-modern.html @@ -92,12 +92,6 @@ 店舗管理 -
  • - - - スタッフ管理 - -
  • @@ -125,4 +119,4 @@ - \ No newline at end of file + diff --git a/src/main/resources/templates/reports/sales.html b/src/main/resources/templates/reports/sales.html index 4f00480..30e95df 100644 --- a/src/main/resources/templates/reports/sales.html +++ b/src/main/resources/templates/reports/sales.html @@ -25,10 +25,10 @@
    -

    +

    売上レポート -

    +
    @@ -255,4 +255,4 @@
    月次レポート
    /*]]>*/ - \ No newline at end of file + diff --git a/src/main/resources/templates/sales/index.html b/src/main/resources/templates/sales/index.html index 2ca0d4c..07d80a9 100644 --- a/src/main/resources/templates/sales/index.html +++ b/src/main/resources/templates/sales/index.html @@ -26,10 +26,10 @@
    -

    +

    売上管理 -

    +
    - \ No newline at end of file + diff --git a/src/main/resources/templates/staffs/index.html b/src/main/resources/templates/staffs/index.html deleted file mode 100644 index 942cdf8..0000000 --- a/src/main/resources/templates/staffs/index.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - - -
    - -
    - -
    -
    -
    -
    -
    -
    -
    -
    -

    - - スタッフ管理 -

    -
    - -
    - - - - - - - - - - - - - - - -
    バーコードスタッフ名登録日時
    - - - - - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - \ No newline at end of file diff --git a/src/test/kotlin/info/nukoneko/kidspos/server/TestConfiguration.kt b/src/test/kotlin/info/nukoneko/kidspos/server/TestConfiguration.kt index b0f3012..2b424f8 100644 --- a/src/test/kotlin/info/nukoneko/kidspos/server/TestConfiguration.kt +++ b/src/test/kotlin/info/nukoneko/kidspos/server/TestConfiguration.kt @@ -3,7 +3,6 @@ package info.nukoneko.kidspos.server import info.nukoneko.kidspos.server.repository.* import info.nukoneko.kidspos.server.service.SaleService import info.nukoneko.kidspos.server.service.SettingService -import info.nukoneko.kidspos.server.service.StaffService import info.nukoneko.kidspos.server.service.StoreService import org.mockito.Mockito.mock import org.springframework.boot.test.context.TestConfiguration @@ -34,10 +33,6 @@ class TestConfiguration { @Primary fun mockStoreRepository(): StoreRepository = mock(StoreRepository::class.java) - @Bean - @Primary - fun mockStaffRepository(): StaffRepository = mock(StaffRepository::class.java) - @Bean @Primary fun mockSettingRepository(): SettingRepository = mock(SettingRepository::class.java) @@ -51,10 +46,6 @@ class TestConfiguration { @Primary fun mockStoreService(): StoreService = mock(StoreService::class.java) - @Bean - @Primary - fun mockStaffService(): StaffService = mock(StaffService::class.java) - @Bean @Primary fun mockSettingService(): SettingService = mock(SettingService::class.java) diff --git a/src/test/kotlin/info/nukoneko/kidspos/server/config/OpenApiTestConfiguration.kt b/src/test/kotlin/info/nukoneko/kidspos/server/config/OpenApiTestConfiguration.kt index 3662b5e..17322d9 100644 --- a/src/test/kotlin/info/nukoneko/kidspos/server/config/OpenApiTestConfiguration.kt +++ b/src/test/kotlin/info/nukoneko/kidspos/server/config/OpenApiTestConfiguration.kt @@ -4,7 +4,6 @@ import info.nukoneko.kidspos.common.service.IdGenerationService import info.nukoneko.kidspos.server.service.* import info.nukoneko.kidspos.server.service.mapper.ItemMapper import info.nukoneko.kidspos.server.service.mapper.SaleMapper -import info.nukoneko.kidspos.server.service.mapper.StaffMapper import info.nukoneko.kidspos.server.service.mapper.StoreMapper import org.mockito.Mockito.mock import org.springframework.boot.test.context.TestConfiguration @@ -35,10 +34,6 @@ class OpenApiTestConfiguration { @Primary fun saleService(): SaleService = mock(SaleService::class.java) - @Bean - @Primary - fun staffService(): StaffService = mock(StaffService::class.java) - @Bean @Primary fun itemMapper(): ItemMapper = mock(ItemMapper::class.java) @@ -47,10 +42,6 @@ class OpenApiTestConfiguration { @Primary fun storeMapper(): StoreMapper = mock(StoreMapper::class.java) - @Bean - @Primary - fun staffMapper(): StaffMapper = mock(StaffMapper::class.java) - @Bean @Primary fun saleMapper(): SaleMapper = mock(SaleMapper::class.java) diff --git a/src/test/kotlin/info/nukoneko/kidspos/server/controller/api/SaleApiControllerTest.kt b/src/test/kotlin/info/nukoneko/kidspos/server/controller/api/SaleApiControllerTest.kt index 74f0aca..05f3f6b 100644 --- a/src/test/kotlin/info/nukoneko/kidspos/server/controller/api/SaleApiControllerTest.kt +++ b/src/test/kotlin/info/nukoneko/kidspos/server/controller/api/SaleApiControllerTest.kt @@ -61,7 +61,6 @@ class SaleApiControllerTest { SaleEntity( id = 1, storeId = 1, - staffId = 1, quantity = 2, amount = 300, deposit = 400, @@ -81,7 +80,6 @@ class SaleApiControllerTest { val request = CreateSaleRequest( storeId = 1, - staffBarcode = "STAFF001", itemIds = "1,2", deposit = 400, ) @@ -99,7 +97,7 @@ class SaleApiControllerTest { `when`(itemParsingService.parseItemsFromIds("1,2")).thenReturn(testItems) `when`(saleProcessingService.processSaleWithValidation(any(), any())) .thenReturn(SaleResult.Success(testSale, summary)) - `when`(receiptService.printReceipt(any(), any(), any(), any())).thenReturn(true) + `when`(receiptService.printReceipt(any(), any(), any())).thenReturn(true) // When & Then mockMvc @@ -120,7 +118,6 @@ class SaleApiControllerTest { val request = CreateSaleRequest( storeId = 1, - staffBarcode = "STAFF001", itemIds = "1,2", deposit = 100, ) @@ -139,7 +136,7 @@ class SaleApiControllerTest { verify(itemParsingService).parseItemsFromIds("1,2") verify(saleProcessingService).processSaleWithValidation(any(), any()) - verify(receiptService, never()).printReceipt(any(), any(), any(), any()) + verify(receiptService, never()).printReceipt(any(), any(), any()) } @Test @@ -148,7 +145,6 @@ class SaleApiControllerTest { val request = CreateSaleRequest( storeId = 1, - staffBarcode = "STAFF001", itemIds = "1,2", deposit = 400, ) @@ -175,7 +171,6 @@ class SaleApiControllerTest { val request = CreateSaleRequest( storeId = 1, - staffBarcode = "STAFF001", itemIds = "1,2", deposit = 400, ) @@ -204,8 +199,6 @@ class SaleApiControllerTest { id = 1, storeId = 1, storeName = "Store 1", - staffId = "STAFF001", - staffName = "Test Staff", totalAmount = 300, deposit = 400, change = 100, @@ -236,8 +229,6 @@ class SaleApiControllerTest { id = 1, storeId = 1, storeName = "Store 1", - staffId = "STAFF001", - staffName = "Test Staff", totalAmount = 300, deposit = 400, change = 100, @@ -302,7 +293,6 @@ class SaleApiControllerTest { """ { "storeId": null, - "staffBarcode": "", "itemIds": "", "deposit": -100 } @@ -323,7 +313,6 @@ class SaleApiControllerTest { val invalidRequest = CreateSaleRequest( storeId = -1, - staffBarcode = "", itemIds = "", deposit = -100, ) diff --git a/src/test/kotlin/info/nukoneko/kidspos/server/controller/api/SaleApiControllerUnitTest.kt b/src/test/kotlin/info/nukoneko/kidspos/server/controller/api/SaleApiControllerUnitTest.kt index 7802e43..92c657d 100644 --- a/src/test/kotlin/info/nukoneko/kidspos/server/controller/api/SaleApiControllerUnitTest.kt +++ b/src/test/kotlin/info/nukoneko/kidspos/server/controller/api/SaleApiControllerUnitTest.kt @@ -44,7 +44,6 @@ class SaleApiControllerUnitTest { SaleEntity( id = 1, storeId = 1, - staffId = 1, quantity = 2, amount = 300, deposit = 400, @@ -64,7 +63,6 @@ class SaleApiControllerUnitTest { val request = CreateSaleRequest( storeId = 1, - staffBarcode = "STAFF001", itemIds = "1,2", deposit = 400, ) @@ -82,7 +80,6 @@ class SaleApiControllerUnitTest { val expectedSaleBean = SaleBean( storeId = request.storeId, - staffBarcode = request.staffBarcode, itemIds = request.itemIds, deposit = request.deposit, ) @@ -90,7 +87,7 @@ class SaleApiControllerUnitTest { `when`(itemParsingService.parseItemsFromIds("1,2")).thenReturn(testItems) `when`(saleProcessingService.processSaleWithValidation(expectedSaleBean, testItems)) .thenReturn(SaleResult.Success(testSale, summary)) - `when`(receiptService.printReceipt(1, testItems, "STAFF001", 400)).thenReturn(true) + `when`(receiptService.printReceipt(1, testItems, 400)).thenReturn(true) // When val result = controller.createSale(request) @@ -108,7 +105,7 @@ class SaleApiControllerUnitTest { verify(itemParsingService).parseItemsFromIds("1,2") verify(saleProcessingService).processSaleWithValidation(expectedSaleBean, testItems) - verify(receiptService).printReceipt(1, testItems, "STAFF001", 400) + verify(receiptService).printReceipt(1, testItems, 400) } @Test @@ -117,7 +114,6 @@ class SaleApiControllerUnitTest { val request = CreateSaleRequest( storeId = 1, - staffBarcode = "STAFF001", itemIds = "1,2", deposit = 100, ) @@ -125,7 +121,6 @@ class SaleApiControllerUnitTest { val expectedSaleBean = SaleBean( storeId = request.storeId, - staffBarcode = request.staffBarcode, itemIds = request.itemIds, deposit = request.deposit, ) @@ -155,7 +150,6 @@ class SaleApiControllerUnitTest { val request = CreateSaleRequest( storeId = 1, - staffBarcode = "STAFF001", itemIds = "1,2", deposit = 400, ) @@ -163,7 +157,6 @@ class SaleApiControllerUnitTest { val expectedSaleBean = SaleBean( storeId = request.storeId, - staffBarcode = request.staffBarcode, itemIds = request.itemIds, deposit = request.deposit, ) @@ -193,7 +186,6 @@ class SaleApiControllerUnitTest { val request = CreateSaleRequest( storeId = 1, - staffBarcode = "STAFF001", itemIds = "1,2", deposit = 400, ) @@ -222,7 +214,6 @@ class SaleApiControllerUnitTest { val request = CreateSaleRequest( storeId = 1, - staffBarcode = "STAFF001", itemIds = "1,2", deposit = 400, ) @@ -230,7 +221,6 @@ class SaleApiControllerUnitTest { val expectedSaleBean = SaleBean( storeId = request.storeId, - staffBarcode = request.staffBarcode, itemIds = request.itemIds, deposit = request.deposit, ) diff --git a/src/test/kotlin/info/nukoneko/kidspos/server/controller/api/StaffApiControllerUnitTest.kt b/src/test/kotlin/info/nukoneko/kidspos/server/controller/api/StaffApiControllerUnitTest.kt deleted file mode 100644 index 2ced3c8..0000000 --- a/src/test/kotlin/info/nukoneko/kidspos/server/controller/api/StaffApiControllerUnitTest.kt +++ /dev/null @@ -1,78 +0,0 @@ -package info.nukoneko.kidspos.server.controller.api - -import info.nukoneko.kidspos.server.domain.exception.ResourceNotFoundException -import info.nukoneko.kidspos.server.entity.StaffEntity -import info.nukoneko.kidspos.server.service.StaffService -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertNotNull -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.InjectMocks -import org.mockito.Mock -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` -import org.mockito.junit.jupiter.MockitoExtension - -@ExtendWith(MockitoExtension::class) -class StaffApiControllerUnitTest { - @Mock - private lateinit var staffService: StaffService - - @InjectMocks - private lateinit var controller: StaffApiController - - @Test - fun `getStaff should return staff when found`() { - // Arrange - val barcode = "ST123456" - val expectedStaff = - StaffEntity( - barcode = barcode, - name = "Test Staff", - ) - `when`(staffService.findStaff(barcode)).thenReturn(expectedStaff) - - // Act - val result = controller.getStaff(barcode) - - // Assert - assertNotNull(result) - assertEquals(200, result.statusCodeValue) - val body = result.body - assertNotNull(body) - assertEquals(barcode, body?.barcode) - assertEquals("Test Staff", body?.name) - verify(staffService).findStaff(barcode) - } - - @Test - fun `getStaff should throw ResourceNotFoundException when staff not found`() { - // Arrange - val barcode = "NONEXISTENT" - `when`(staffService.findStaff(barcode)).thenReturn(null) - - // Act & Assert - val exception = - assertThrows { - controller.getStaff(barcode) - } - assertEquals("Staff with barcode $barcode not found", exception.message) - verify(staffService).findStaff(barcode) - } - - @Test - fun `getStaff should throw ResourceNotFoundException for empty barcode`() { - // Arrange - val barcode = "" - `when`(staffService.findStaff(barcode)).thenReturn(null) - - // Act & Assert - val exception = - assertThrows { - controller.getStaff(barcode) - } - assertEquals("Staff with barcode not found", exception.message) - verify(staffService).findStaff(barcode) - } -} diff --git a/src/test/kotlin/info/nukoneko/kidspos/server/repository/QueryOptimizationTest.kt b/src/test/kotlin/info/nukoneko/kidspos/server/repository/QueryOptimizationTest.kt index a1c630c..d08aecd 100644 --- a/src/test/kotlin/info/nukoneko/kidspos/server/repository/QueryOptimizationTest.kt +++ b/src/test/kotlin/info/nukoneko/kidspos/server/repository/QueryOptimizationTest.kt @@ -81,7 +81,6 @@ class QueryOptimizationTest { SaleEntity( id = 1, storeId = 1, - staffId = 1, quantity = 3, amount = 300, deposit = 500, diff --git a/src/test/kotlin/info/nukoneko/kidspos/server/repository/SaleRepositoryTest.kt b/src/test/kotlin/info/nukoneko/kidspos/server/repository/SaleRepositoryTest.kt index 57f2584..0581944 100644 --- a/src/test/kotlin/info/nukoneko/kidspos/server/repository/SaleRepositoryTest.kt +++ b/src/test/kotlin/info/nukoneko/kidspos/server/repository/SaleRepositoryTest.kt @@ -34,7 +34,6 @@ class SaleRepositoryTest { SaleEntity( id = 0, storeId = 1, - staffId = 1, quantity = 2, amount = 300, deposit = 400, @@ -44,7 +43,6 @@ class SaleRepositoryTest { SaleEntity( id = 0, storeId = 1, - staffId = 2, quantity = 3, amount = 500, deposit = 500, @@ -54,7 +52,6 @@ class SaleRepositoryTest { SaleEntity( id = 0, storeId = 2, - staffId = 1, quantity = 1, amount = 200, deposit = 300, @@ -84,7 +81,6 @@ class SaleRepositoryTest { SaleEntity( id = 0, storeId = 3, - staffId = 3, quantity = 4, amount = 600, deposit = 700, @@ -233,7 +229,6 @@ class SaleRepositoryTest { SaleEntity( id = testSale1.id, storeId = testSale1.storeId, - staffId = testSale1.staffId, quantity = testSale1.quantity, amount = 350, deposit = testSale1.deposit, diff --git a/src/test/kotlin/info/nukoneko/kidspos/server/repository/StaffRepositoryTest.kt b/src/test/kotlin/info/nukoneko/kidspos/server/repository/StaffRepositoryTest.kt deleted file mode 100644 index 479f67e..0000000 --- a/src/test/kotlin/info/nukoneko/kidspos/server/repository/StaffRepositoryTest.kt +++ /dev/null @@ -1,387 +0,0 @@ -package info.nukoneko.kidspos.server.repository - -import info.nukoneko.kidspos.server.entity.StaffEntity -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.* -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest -import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager -import org.springframework.test.context.ActiveProfiles - -/** - * Integration tests for StaffRepository - * - * Tests data access layer operations for staff entities using - * @DataJpaTest annotation for lightweight database testing with - * automatic rollback after each test. - * - * Part of Task 7.2: Repository layer integration tests - */ -@DataJpaTest -@ActiveProfiles("test") -@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) -@DisplayName("StaffRepository Integration Tests") -@Disabled("Spring context not configured") -class StaffRepositoryTest { - @Autowired - private lateinit var staffRepository: StaffRepository - - @Autowired - private lateinit var entityManager: TestEntityManager - - @BeforeEach - fun setUp() { - // Clean up before each test - staffRepository.deleteAll() - entityManager.flush() - entityManager.clear() - } - - @Nested - @DisplayName("Save Operations") - inner class SaveOperations { - @Test - @DisplayName("Should save new staff member") - fun shouldSaveNewStaffMember() { - // Given - val staff = - StaffEntity( - barcode = "STAFF001", - name = "Test Staff", - ) - - // When - val savedStaff = staffRepository.save(staff) - entityManager.flush() - entityManager.clear() - - // Then - assertThat(savedStaff).isNotNull - assertThat(savedStaff.barcode).isEqualTo("STAFF001") - assertThat(savedStaff.name).isEqualTo("Test Staff") - - val foundStaff = staffRepository.findById("STAFF001").orElse(null) - assertThat(foundStaff).isNotNull - assertThat(foundStaff.name).isEqualTo("Test Staff") - } - - @Test - @DisplayName("Should update existing staff member") - fun shouldUpdateExistingStaffMember() { - // Given - val staff = - StaffEntity( - barcode = "STAFF002", - name = "Original Name", - ) - staffRepository.save(staff) - entityManager.flush() - entityManager.clear() - - // When - val updatedStaff = - StaffEntity( - barcode = "STAFF002", - name = "Updated Name", - ) - val result = staffRepository.save(updatedStaff) - entityManager.flush() - entityManager.clear() - - // Then - assertThat(result.name).isEqualTo("Updated Name") - - val foundStaff = staffRepository.findById("STAFF002").orElse(null) - assertThat(foundStaff).isNotNull - assertThat(foundStaff.name).isEqualTo("Updated Name") - } - - @Test - @DisplayName("Should save multiple staff members") - fun shouldSaveMultipleStaffMembers() { - // Given - val staffList = - listOf( - StaffEntity("STAFF003", "Staff One"), - StaffEntity("STAFF004", "Staff Two"), - StaffEntity("STAFF005", "Staff Three"), - ) - - // When - val savedStaff = staffRepository.saveAll(staffList) - entityManager.flush() - entityManager.clear() - - // Then - assertThat(savedStaff).hasSize(3) - assertThat(staffRepository.count()).isEqualTo(3) - - val allStaff = staffRepository.findAll() - assertThat(allStaff).hasSize(3) - assertThat(allStaff.map { it.barcode }).containsExactlyInAnyOrder( - "STAFF003", - "STAFF004", - "STAFF005", - ) - } - } - - @Nested - @DisplayName("Find Operations") - inner class FindOperations { - @BeforeEach - fun setUpTestData() { - val staffList = - listOf( - StaffEntity("FIND001", "Alice"), - StaffEntity("FIND002", "Bob"), - StaffEntity("FIND003", "Charlie"), - ) - staffRepository.saveAll(staffList) - entityManager.flush() - entityManager.clear() - } - - @Test - @DisplayName("Should find staff by barcode") - fun shouldFindStaffByBarcode() { - // When - val staff = staffRepository.findById("FIND002").orElse(null) - - // Then - assertThat(staff).isNotNull - assertThat(staff.barcode).isEqualTo("FIND002") - assertThat(staff.name).isEqualTo("Bob") - } - - @Test - @DisplayName("Should return empty when staff not found") - fun shouldReturnEmptyWhenStaffNotFound() { - // When - val staff = staffRepository.findById("NONEXISTENT") - - // Then - assertThat(staff.isPresent).isFalse - } - - @Test - @DisplayName("Should find all staff members") - fun shouldFindAllStaffMembers() { - // When - val allStaff = staffRepository.findAll() - - // Then - assertThat(allStaff).hasSize(3) - assertThat(allStaff.map { it.name }).containsExactlyInAnyOrder( - "Alice", - "Bob", - "Charlie", - ) - } - - @Test - @DisplayName("Should check if staff exists by barcode") - fun shouldCheckIfStaffExistsByBarcode() { - // When & Then - assertThat(staffRepository.existsById("FIND001")).isTrue - assertThat(staffRepository.existsById("FIND002")).isTrue - assertThat(staffRepository.existsById("NONEXISTENT")).isFalse - } - } - - @Nested - @DisplayName("Delete Operations") - inner class DeleteOperations { - @BeforeEach - fun setUpTestData() { - val staffList = - listOf( - StaffEntity("DEL001", "Staff to Delete 1"), - StaffEntity("DEL002", "Staff to Delete 2"), - StaffEntity("DEL003", "Staff to Keep"), - ) - staffRepository.saveAll(staffList) - entityManager.flush() - entityManager.clear() - } - - @Test - @DisplayName("Should delete staff by barcode") - fun shouldDeleteStaffByBarcode() { - // Given - assertThat(staffRepository.existsById("DEL001")).isTrue - - // When - staffRepository.deleteById("DEL001") - entityManager.flush() - entityManager.clear() - - // Then - assertThat(staffRepository.existsById("DEL001")).isFalse - assertThat(staffRepository.count()).isEqualTo(2) - } - - @Test - @DisplayName("Should delete staff entity") - fun shouldDeleteStaffEntity() { - // Given - val staffToDelete = staffRepository.findById("DEL002").orElseThrow() - - // When - staffRepository.delete(staffToDelete) - entityManager.flush() - entityManager.clear() - - // Then - assertThat(staffRepository.existsById("DEL002")).isFalse - assertThat(staffRepository.count()).isEqualTo(2) - } - - @Test - @DisplayName("Should delete all staff") - fun shouldDeleteAllStaff() { - // Given - assertThat(staffRepository.count()).isEqualTo(3) - - // When - staffRepository.deleteAll() - entityManager.flush() - entityManager.clear() - - // Then - assertThat(staffRepository.count()).isEqualTo(0) - assertThat(staffRepository.findAll()).isEmpty() - } - - @Test - @DisplayName("Should handle delete non-existent staff gracefully") - fun shouldHandleDeleteNonExistentStaffGracefully() { - // When - delete non-existent entity (JPA throws EmptyResultDataAccessException) - try { - staffRepository.deleteById("NONEXISTENT") - entityManager.flush() - } catch (e: Exception) { - // Expected - JPA throws exception for non-existent entities - // This is standard JPA behavior - } - - // Then - Verify other data is intact - assertThat(staffRepository.count()).isEqualTo(3) - } - } - - @Nested - @DisplayName("Batch Operations") - inner class BatchOperations { - @Test - @DisplayName("Should count staff correctly") - fun shouldCountStaffCorrectly() { - // Given - assertThat(staffRepository.count()).isEqualTo(0) - - // When - staffRepository.saveAll( - listOf( - StaffEntity("COUNT001", "Staff 1"), - StaffEntity("COUNT002", "Staff 2"), - StaffEntity("COUNT003", "Staff 3"), - ), - ) - entityManager.flush() - entityManager.clear() - - // Then - assertThat(staffRepository.count()).isEqualTo(3) - } - - @Test - @DisplayName("Should delete batch by IDs") - fun shouldDeleteBatchByIds() { - // Given - staffRepository.saveAll( - listOf( - StaffEntity("BATCH001", "Staff 1"), - StaffEntity("BATCH002", "Staff 2"), - StaffEntity("BATCH003", "Staff 3"), - StaffEntity("BATCH004", "Staff 4"), - ), - ) - entityManager.flush() - entityManager.clear() - - // When - val toDelete = - listOf( - StaffEntity("BATCH001", "Staff 1"), - StaffEntity("BATCH003", "Staff 3"), - ) - staffRepository.deleteAll(toDelete) - entityManager.flush() - entityManager.clear() - - // Then - assertThat(staffRepository.count()).isEqualTo(2) - assertThat(staffRepository.existsById("BATCH002")).isTrue - assertThat(staffRepository.existsById("BATCH004")).isTrue - } - - @Test - @DisplayName("Should find all by IDs") - fun shouldFindAllByIds() { - // Given - staffRepository.saveAll( - listOf( - StaffEntity("FINDALL001", "Staff A"), - StaffEntity("FINDALL002", "Staff B"), - StaffEntity("FINDALL003", "Staff C"), - StaffEntity("FINDALL004", "Staff D"), - ), - ) - entityManager.flush() - entityManager.clear() - - // When - val idsToFind = listOf("FINDALL001", "FINDALL003", "FINDALL004", "NONEXISTENT") - val foundStaff = staffRepository.findAllById(idsToFind) - - // Then - assertThat(foundStaff).hasSize(3) - assertThat(foundStaff.map { it.barcode }).containsExactlyInAnyOrder( - "FINDALL001", - "FINDALL003", - "FINDALL004", - ) - } - } - - @Nested - @DisplayName("Transaction and Rollback") - inner class TransactionTests { - @Test - @DisplayName("Should rollback transaction on failure") - fun shouldRollbackTransactionOnFailure() { - // Given - val initialCount = staffRepository.count() - - // When - Simulate a transaction that fails - try { - staffRepository.save(StaffEntity("TRANS001", "Transaction Test")) - entityManager.flush() - - // Force a constraint violation or exception - // This would normally be done in a @Transactional method - staffRepository.save(StaffEntity("TRANS001", "Duplicate")) // Same ID - entityManager.flush() - } catch (e: Exception) { - // Expected exception - entityManager.clear() - } - - // Then - Verify rollback (in real scenario with proper transaction management) - // Note: @DataJpaTest automatically rolls back each test - val finalCount = staffRepository.count() - assertThat(finalCount).isEqualTo(initialCount + 1) // Only first save persisted - } - } -} diff --git a/src/test/kotlin/info/nukoneko/kidspos/server/service/ReceiptServiceTest.kt b/src/test/kotlin/info/nukoneko/kidspos/server/service/ReceiptServiceTest.kt index 1270860..c258c3d 100644 --- a/src/test/kotlin/info/nukoneko/kidspos/server/service/ReceiptServiceTest.kt +++ b/src/test/kotlin/info/nukoneko/kidspos/server/service/ReceiptServiceTest.kt @@ -2,7 +2,6 @@ package info.nukoneko.kidspos.server.service import info.nukoneko.kidspos.server.config.AppProperties import info.nukoneko.kidspos.server.controller.dto.request.ItemBean -import info.nukoneko.kidspos.server.entity.StaffEntity import info.nukoneko.kidspos.server.entity.StoreEntity import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.BeforeEach @@ -19,9 +18,6 @@ class ReceiptServiceTest { @MockBean private lateinit var storeService: StoreService - @MockBean - private lateinit var staffService: StaffService - @MockBean private lateinit var appProperties: AppProperties @@ -29,7 +25,7 @@ class ReceiptServiceTest { @BeforeEach fun setup() { - receiptService = ReceiptService(storeService, staffService, appProperties) + receiptService = ReceiptService(storeService, appProperties) // Mock AppProperties - skip for now due to complexity } @@ -92,7 +88,6 @@ class ReceiptServiceTest { fun `should generate receipt content with proper formatting`() { // Given val storeId = 1 - val staffBarcode = "STAFF001" val deposit = 1000 val items = listOf( @@ -101,32 +96,27 @@ class ReceiptServiceTest { ) val store = StoreEntity(storeId, "Test Store", "192.168.1.100") - val staff = StaffEntity(staffBarcode, "Test Staff") `when`(storeService.findStore(storeId)).thenReturn(store) - `when`(staffService.findStaff(staffBarcode)).thenReturn(staff) // When - val result = receiptService.generateReceiptContent(storeId, items, staffBarcode, deposit) + val result = receiptService.generateReceiptContent(storeId, items, deposit) // Then assertNotNull(result) assertTrue(result.contains("Test Store")) - assertTrue(result.contains("Test Staff")) assertTrue(result.contains("Item 1 - 300リバー")) assertTrue(result.contains("Item 2 - 400リバー")) assertTrue(result.contains("Total: 700リバー")) assertTrue(result.contains("Deposit: 1000リバー")) assertTrue(result.contains("Change: 300リバー")) verify(storeService).findStore(storeId) - verify(staffService).findStaff(staffBarcode) } @Test fun `should generate receipt content with unknown store and staff`() { // Given val storeId = 999 - val staffBarcode = "UNKNOWN" val deposit = 500 val items = listOf( @@ -134,39 +124,33 @@ class ReceiptServiceTest { ) `when`(storeService.findStore(storeId)).thenReturn(null) - `when`(staffService.findStaff(staffBarcode)).thenReturn(null) // When - val result = receiptService.generateReceiptContent(storeId, items, staffBarcode, deposit) + val result = receiptService.generateReceiptContent(storeId, items, deposit) // Then assertNotNull(result) assertTrue(result.contains("Unknown Store")) - assertTrue(result.contains("Unknown Staff")) assertTrue(result.contains("Test Item - 200リバー")) assertTrue(result.contains("Total: 200リバー")) assertTrue(result.contains("Deposit: 500リバー")) assertTrue(result.contains("Change: 300リバー")) verify(storeService).findStore(storeId) - verify(staffService).findStaff(staffBarcode) } @Test fun `should handle empty items list in receipt generation`() { // Given val storeId = 1 - val staffBarcode = "STAFF001" val deposit = 100 val items = emptyList() val store = StoreEntity(storeId, "Test Store", "192.168.1.100") - val staff = StaffEntity(staffBarcode, "Test Staff") `when`(storeService.findStore(storeId)).thenReturn(store) - `when`(staffService.findStaff(staffBarcode)).thenReturn(staff) // When - val result = receiptService.generateReceiptContent(storeId, items, staffBarcode, deposit) + val result = receiptService.generateReceiptContent(storeId, items, deposit) // Then assertNotNull(result) @@ -174,6 +158,5 @@ class ReceiptServiceTest { assertTrue(result.contains("Deposit: 100リバー")) assertTrue(result.contains("Change: 100リバー")) verify(storeService).findStore(storeId) - verify(staffService).findStaff(staffBarcode) } } diff --git a/src/test/kotlin/info/nukoneko/kidspos/server/service/SaleProcessingServiceTest.kt b/src/test/kotlin/info/nukoneko/kidspos/server/service/SaleProcessingServiceTest.kt index 2d857ce..adcd150 100644 --- a/src/test/kotlin/info/nukoneko/kidspos/server/service/SaleProcessingServiceTest.kt +++ b/src/test/kotlin/info/nukoneko/kidspos/server/service/SaleProcessingServiceTest.kt @@ -40,7 +40,7 @@ class SaleProcessingServiceTest { @Test fun `should process sale with multiple items correctly`() { // Given - val saleBean = SaleBean(storeId = 1, staffBarcode = "1001", itemIds = "1,2,3", deposit = 1000) + val saleBean = SaleBean(storeId = 1, itemIds = "1,2,3", deposit = 1000) val items = listOf( ItemBean(1, "001", "Item 1", 300), @@ -52,7 +52,6 @@ class SaleProcessingServiceTest { SaleEntity( id = 1, storeId = 1, - staffId = 1, quantity = 3, amount = 900, deposit = 1000, @@ -90,7 +89,7 @@ class SaleProcessingServiceTest { @Test fun `should handle duplicate items correctly`() { // Given - val saleBean = SaleBean(storeId = 1, staffBarcode = "1001", itemIds = "1,1,2", deposit = 800) + val saleBean = SaleBean(storeId = 1, itemIds = "1,1,2", deposit = 800) val items = listOf( ItemBean(1, "001", "Item 1", 300), @@ -204,7 +203,7 @@ class SaleValidationServiceTest { @Test fun `should validate valid sale request`() { // Given - val saleBean = SaleBean(storeId = 1, staffBarcode = "1001", itemIds = "1,2", deposit = 500) + val saleBean = SaleBean(storeId = 1, itemIds = "1,2", deposit = 500) val items = listOf( ItemBean(1, "001", "Item 1", 200), @@ -220,7 +219,7 @@ class SaleValidationServiceTest { @Test fun `should throw exception for insufficient deposit`() { // Given - val saleBean = SaleBean(storeId = 1, staffBarcode = "1001", itemIds = "1,2", deposit = 300) + val saleBean = SaleBean(storeId = 1, itemIds = "1,2", deposit = 300) val items = listOf( ItemBean(1, "001", "Item 1", 200), @@ -236,7 +235,7 @@ class SaleValidationServiceTest { @Test fun `should throw exception for empty items`() { // Given - val saleBean = SaleBean(storeId = 1, staffBarcode = "1001", itemIds = "", deposit = 1000) + val saleBean = SaleBean(storeId = 1, itemIds = "", deposit = 1000) val items = emptyList() // When & Then diff --git a/src/test/kotlin/info/nukoneko/kidspos/server/service/StaffServiceTest.kt b/src/test/kotlin/info/nukoneko/kidspos/server/service/StaffServiceTest.kt deleted file mode 100644 index 48b2dad..0000000 --- a/src/test/kotlin/info/nukoneko/kidspos/server/service/StaffServiceTest.kt +++ /dev/null @@ -1,118 +0,0 @@ -package info.nukoneko.kidspos.server.service - -import info.nukoneko.kidspos.server.entity.StaffEntity -import info.nukoneko.kidspos.server.repository.StaffRepository -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.Test -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.boot.test.mock.mockito.MockBean -import java.util.* - -@SpringBootTest -@Disabled("Spring context not configured") -class StaffServiceTest { - @MockBean - private lateinit var staffRepository: StaffRepository - - private lateinit var staffService: StaffService - - @BeforeEach - fun setup() { - staffService = StaffService(staffRepository) - } - - @Test - fun `should find staff by barcode when staff exists`() { - // Given - val barcode = "STAFF001" - val expectedStaff = - StaffEntity( - barcode = barcode, - name = "Test Staff", - ) - `when`(staffRepository.findById(barcode)).thenReturn(Optional.of(expectedStaff)) - - // When - val result = staffService.findStaff(barcode) - - // Then - assertNotNull(result) - assertEquals(barcode, result?.barcode) - assertEquals("Test Staff", result?.name) - verify(staffRepository).findById(barcode) - } - - @Test - fun `should return null when staff does not exist by barcode`() { - // Given - val barcode = "NONEXISTENT" - `when`(staffRepository.findById(barcode)).thenReturn(Optional.empty()) - - // When - val result = staffService.findStaff(barcode) - - // Then - assertNull(result) - verify(staffRepository).findById(barcode) - } - - @Test - fun `should find all staff`() { - // Given - val expectedStaff = - listOf( - StaffEntity(barcode = "STAFF001", name = "Staff Member 1"), - StaffEntity(barcode = "STAFF002", name = "Staff Member 2"), - ) - `when`(staffRepository.findAll()).thenReturn(expectedStaff) - - // When - val result = staffService.findAll() - - // Then - assertNotNull(result) - assertEquals(2, result.size) - assertEquals("Staff Member 1", result[0].name) - assertEquals("Staff Member 2", result[1].name) - verify(staffRepository).findAll() - } - - @Test - fun `should handle empty staff list`() { - // Given - `when`(staffRepository.findAll()).thenReturn(emptyList()) - - // When - val result = staffService.findAll() - - // Then - assertNotNull(result) - assertTrue(result.isEmpty()) - verify(staffRepository).findAll() - } - - @Test - fun `should handle special characters in barcode`() { - // Given - val barcodeWithSpecialChars = "STAFF-001_TEST" - val expectedStaff = - StaffEntity( - barcode = barcodeWithSpecialChars, - name = "Special Staff", - ) - `when`(staffRepository.findById(barcodeWithSpecialChars)).thenReturn(Optional.of(expectedStaff)) - - // When - val result = staffService.findStaff(barcodeWithSpecialChars) - - // Then - assertNotNull(result) - assertEquals(barcodeWithSpecialChars, result?.barcode) - assertEquals("Special Staff", result?.name) - verify(staffRepository).findById(barcodeWithSpecialChars) - } -} diff --git a/src/test/kotlin/info/nukoneko/kidspos/server/service/ValidationServiceUnitTest.kt b/src/test/kotlin/info/nukoneko/kidspos/server/service/ValidationServiceUnitTest.kt index 7b0b715..a067b35 100644 --- a/src/test/kotlin/info/nukoneko/kidspos/server/service/ValidationServiceUnitTest.kt +++ b/src/test/kotlin/info/nukoneko/kidspos/server/service/ValidationServiceUnitTest.kt @@ -2,7 +2,6 @@ package info.nukoneko.kidspos.server.service import info.nukoneko.kidspos.server.domain.exception.ValidationException import info.nukoneko.kidspos.server.repository.ItemRepository -import info.nukoneko.kidspos.server.repository.StaffRepository import info.nukoneko.kidspos.server.repository.StoreRepository import org.junit.jupiter.api.Assertions.assertDoesNotThrow import org.junit.jupiter.api.Assertions.assertEquals @@ -23,9 +22,6 @@ class ValidationServiceUnitTest { @Mock private lateinit var storeRepository: StoreRepository - @Mock - private lateinit var staffRepository: StaffRepository - @InjectMocks private lateinit var validationService: ValidationService diff --git a/tests/vrt/pages.spec.ts b/tests/vrt/pages.spec.ts index 1e3fc74..7ec3433 100644 --- a/tests/vrt/pages.spec.ts +++ b/tests/vrt/pages.spec.ts @@ -36,15 +36,6 @@ test.describe('Visual Regression Tests', () => { }); }); - test('Staff Management Page', async ({ page }) => { - await page.goto('/staffs'); - await page.waitForSelector('h1'); - - await expect(page).toHaveScreenshot('staff-page.png', { - fullPage: true, - animations: 'disabled', - }); - }); test('Sales Report Page', async ({ page }) => { await page.goto('/reports/sales'); @@ -119,4 +110,4 @@ test.describe('Interactive Elements', () => { animations: 'disabled', }); }); -}); \ No newline at end of file +}); diff --git a/tests/vrt/pages.spec.ts-snapshots/homepage-chromium-linux.png b/tests/vrt/pages.spec.ts-snapshots/homepage-chromium-linux.png index db4bd81..eba8083 100644 Binary files a/tests/vrt/pages.spec.ts-snapshots/homepage-chromium-linux.png and b/tests/vrt/pages.spec.ts-snapshots/homepage-chromium-linux.png differ diff --git a/tests/vrt/pages.spec.ts-snapshots/items-page-chromium-linux.png b/tests/vrt/pages.spec.ts-snapshots/items-page-chromium-linux.png index dabfffc..abb75b4 100644 Binary files a/tests/vrt/pages.spec.ts-snapshots/items-page-chromium-linux.png and b/tests/vrt/pages.spec.ts-snapshots/items-page-chromium-linux.png differ diff --git a/tests/vrt/pages.spec.ts-snapshots/mobile-homepage-chromium-linux.png b/tests/vrt/pages.spec.ts-snapshots/mobile-homepage-chromium-linux.png index 156dbee..b738954 100644 Binary files a/tests/vrt/pages.spec.ts-snapshots/mobile-homepage-chromium-linux.png and b/tests/vrt/pages.spec.ts-snapshots/mobile-homepage-chromium-linux.png differ diff --git a/tests/vrt/pages.spec.ts-snapshots/mobile-sales-report-chromium-linux.png b/tests/vrt/pages.spec.ts-snapshots/mobile-sales-report-chromium-linux.png index 49da595..950c90f 100644 Binary files a/tests/vrt/pages.spec.ts-snapshots/mobile-sales-report-chromium-linux.png and b/tests/vrt/pages.spec.ts-snapshots/mobile-sales-report-chromium-linux.png differ diff --git a/tests/vrt/pages.spec.ts-snapshots/sales-page-chromium-linux.png b/tests/vrt/pages.spec.ts-snapshots/sales-page-chromium-linux.png index aa041ea..a79f94c 100644 Binary files a/tests/vrt/pages.spec.ts-snapshots/sales-page-chromium-linux.png and b/tests/vrt/pages.spec.ts-snapshots/sales-page-chromium-linux.png differ diff --git a/tests/vrt/pages.spec.ts-snapshots/sales-report-page-chromium-linux.png b/tests/vrt/pages.spec.ts-snapshots/sales-report-page-chromium-linux.png index 7dc68ba..92aae18 100644 Binary files a/tests/vrt/pages.spec.ts-snapshots/sales-report-page-chromium-linux.png and b/tests/vrt/pages.spec.ts-snapshots/sales-report-page-chromium-linux.png differ diff --git a/tests/vrt/pages.spec.ts-snapshots/stores-page-chromium-linux.png b/tests/vrt/pages.spec.ts-snapshots/stores-page-chromium-linux.png index 1304a51..26477e4 100644 Binary files a/tests/vrt/pages.spec.ts-snapshots/stores-page-chromium-linux.png and b/tests/vrt/pages.spec.ts-snapshots/stores-page-chromium-linux.png differ