From 48f1b6a113de9cbc53a4d12f2cf536e3113f9d4a Mon Sep 17 00:00:00 2001 From: developer Date: Wed, 9 Sep 2026 12:49:16 +0500 Subject: [PATCH 1/2] invoice fix --- .../crm/entity/dto/InvoiceCreateDto.java | 9 +++++--- .../dto/transaction/TransactionCreateDto.java | 3 +-- .../org/example/crm/entity/model/Invoice.java | 4 ++-- .../org/example/crm/exceptions/ErrorType.java | 3 ++- .../org/example/crm/mapper/InvoiceMapper.java | 8 ++++++- .../crm/repository/GroupLevelRepository.java | 4 ++++ .../crm/repository/InvoiceRepository.java | 10 +++++++++ .../example/crm/service/InvoiceService.java | 19 +++++++++++++--- .../example/crm/service/LessonService.java | 22 ++++++++----------- .../crm/validator/EnrollmentValidator.java | 8 ++++++- .../crm/validator/GroupLevelValidator.java | 15 +++++++++---- 11 files changed, 75 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/example/crm/entity/dto/InvoiceCreateDto.java b/src/main/java/org/example/crm/entity/dto/InvoiceCreateDto.java index e322311..ec50e86 100644 --- a/src/main/java/org/example/crm/entity/dto/InvoiceCreateDto.java +++ b/src/main/java/org/example/crm/entity/dto/InvoiceCreateDto.java @@ -1,12 +1,15 @@ package org.example.crm.entity.dto; +import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; -import org.example.crm.entity.model.Enrollment; import java.math.BigDecimal; public record InvoiceCreateDto( - @NotNull Enrollment enrollment, - @NotNull BigDecimal amount + @NotNull String enrollmentId, + @NotNull BigDecimal amount, + @NotNull String levelId, + @Min(value = 1) + @NotNull Integer month ) { } diff --git a/src/main/java/org/example/crm/entity/dto/transaction/TransactionCreateDto.java b/src/main/java/org/example/crm/entity/dto/transaction/TransactionCreateDto.java index 2e7f4a1..08b8037 100644 --- a/src/main/java/org/example/crm/entity/dto/transaction/TransactionCreateDto.java +++ b/src/main/java/org/example/crm/entity/dto/transaction/TransactionCreateDto.java @@ -9,11 +9,10 @@ public record TransactionCreateDto( @NotNull - @Schema(allowableValues = {"PAID", "RETURNED"}) + @Schema(allowableValues = {"PAID", "RETURNED","MONTHLY_FEE"}) TransactionType type, @NotNull - @DecimalMin(value = "0.0", message = "amount cannot be negative") BigDecimal amount, @NotNull diff --git a/src/main/java/org/example/crm/entity/model/Invoice.java b/src/main/java/org/example/crm/entity/model/Invoice.java index 2792964..26131fd 100644 --- a/src/main/java/org/example/crm/entity/model/Invoice.java +++ b/src/main/java/org/example/crm/entity/model/Invoice.java @@ -7,10 +7,8 @@ import lombok.AllArgsConstructor; import org.example.crm.entity.base.BaseEntity; import org.example.crm.entity.enums.InvoiceStatus; -import org.example.crm.entity.enums.InvoiceType; import java.math.BigDecimal; -import java.time.LocalDateTime; @Entity @Table(name = "invoices") @@ -32,5 +30,7 @@ public class Invoice extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY, optional = false) private Enrollment enrollment; + private String level; + private Integer month; } \ No newline at end of file diff --git a/src/main/java/org/example/crm/exceptions/ErrorType.java b/src/main/java/org/example/crm/exceptions/ErrorType.java index 0e37d4a..e384643 100644 --- a/src/main/java/org/example/crm/exceptions/ErrorType.java +++ b/src/main/java/org/example/crm/exceptions/ErrorType.java @@ -63,7 +63,8 @@ public enum ErrorType { STUDENT_ALREADY_ENROLLED_TO_THIS_GROUP("student.already.enrolled.to.this.group"), COURSE_NOT_FOUND("course.not.found"), COURSE_ALREADY_EXISTS("course.already.exists"), INVALID_INPUT("invalid.input"), - TRANSACTION_NOT_FOUND("transaction.not.found"),; + TRANSACTION_NOT_FOUND("transaction.not.found"), + INVOICE_ALREADY_CREATED("invoice.already.created"); private final String key; diff --git a/src/main/java/org/example/crm/mapper/InvoiceMapper.java b/src/main/java/org/example/crm/mapper/InvoiceMapper.java index 762b394..945c0d0 100644 --- a/src/main/java/org/example/crm/mapper/InvoiceMapper.java +++ b/src/main/java/org/example/crm/mapper/InvoiceMapper.java @@ -9,6 +9,8 @@ import org.example.crm.entity.model.*; import org.example.crm.projection.InvoiceProjection; import org.example.crm.service.InvoiceNumberService; +import org.example.crm.validator.EnrollmentValidator; +import org.example.crm.validator.GroupLevelValidator; import org.springframework.stereotype.Component; @Component @@ -16,13 +18,17 @@ public class InvoiceMapper { final InvoiceNumberService invoiceNumberService; final EnrollmentMapper enrollmentMapper; + private final EnrollmentValidator enrollmentValidator; + private final GroupLevelValidator groupLevelValidator; public Invoice toEntity(InvoiceCreateDto createDto) { Invoice invoice = new Invoice(); invoice.setInvoiceNumber(invoiceNumberService.generateInvoiceNumber()); invoice.setAmount(createDto.amount()); - invoice.setEnrollment(createDto.enrollment()); + invoice.setEnrollment(enrollmentValidator.validateIdAndGet(createDto.enrollmentId())); + invoice.setLevel(groupLevelValidator.validateIdAndGetName(createDto.levelId())); + invoice.setMonth(createDto.month()); invoice.setPaymentStatus(InvoiceStatus.PENDING); return invoice; } diff --git a/src/main/java/org/example/crm/repository/GroupLevelRepository.java b/src/main/java/org/example/crm/repository/GroupLevelRepository.java index 1f4bece..1c2c906 100644 --- a/src/main/java/org/example/crm/repository/GroupLevelRepository.java +++ b/src/main/java/org/example/crm/repository/GroupLevelRepository.java @@ -1,5 +1,6 @@ package org.example.crm.repository; +import jakarta.validation.constraints.NotNull; import org.example.crm.entity.model.Level; import org.example.crm.projection.LevelNamesProjection; import org.springframework.data.jpa.repository.JpaRepository; @@ -99,4 +100,7 @@ Optional getFirstLevelForGroup( @Query("select l from Level l where l.organizationId=:orgId and l.id=:id and l.deleted=false ") Optional findById(@Param("id") String id, @Param("orgId") String organizationId); + + @Query("select l.name from Level l where l.id=:id and l.organizationId=:orgId and l.deleted=false") + Optional checkAndGetName(@Param("id") String levelId,@Param("orgId")String organizationId); } diff --git a/src/main/java/org/example/crm/repository/InvoiceRepository.java b/src/main/java/org/example/crm/repository/InvoiceRepository.java index 58dfec1..71ac243 100644 --- a/src/main/java/org/example/crm/repository/InvoiceRepository.java +++ b/src/main/java/org/example/crm/repository/InvoiceRepository.java @@ -99,4 +99,14 @@ AnalyticInvoiceProjection getAnalyticInvoice(String organizationId, @Transactional @Query("update Invoice i set i.deleted = true where i.id =:id") void softDelete(String id); + + @Query(""" + select exists ( + select i.id from Invoice i + join i.enrollment e + where e.group.id=:groupId + and i.level=:levelName + and i.month=:month + and i.deleted=false)""") + boolean checkIfAlreadyCreated(String groupId, String levelName, Integer currentMonth); } diff --git a/src/main/java/org/example/crm/service/InvoiceService.java b/src/main/java/org/example/crm/service/InvoiceService.java index becbab2..84e32aa 100644 --- a/src/main/java/org/example/crm/service/InvoiceService.java +++ b/src/main/java/org/example/crm/service/InvoiceService.java @@ -8,6 +8,7 @@ import org.example.crm.entity.enums.InvoiceStatus; import org.example.crm.entity.enums.TransactionType; import org.example.crm.entity.model.Enrollment; +import org.example.crm.entity.model.Group; import org.example.crm.entity.model.Invoice; import org.example.crm.exceptions.ErrorCodes; import org.example.crm.exceptions.ErrorType; @@ -17,6 +18,7 @@ import org.example.crm.repository.EnrollmentRepository; import org.example.crm.repository.InvoiceRepository; import org.example.crm.repository.StudentRepository; +import org.example.crm.validator.GroupValidator; import org.example.crm.validator.InvoiceValidator; import org.example.crm.validator.UserValidator; import org.springframework.data.domain.Page; @@ -38,14 +40,16 @@ public class InvoiceService extends AbstractService< private final EnrollmentRepository enrollmentRepository; private final UserValidator userValidator; private final TransactionService transactionService; + private final GroupValidator groupValidator; - protected InvoiceService(InvoiceRepository repository, InvoiceMapper mapper, InvoiceValidator validator, StudentRepository studentRepository, EnrollmentService enrollmentService, EnrollmentRepository enrollmentRepository, UserValidator userValidator, TransactionService transactionService) { + protected InvoiceService(InvoiceRepository repository, InvoiceMapper mapper, InvoiceValidator validator, StudentRepository studentRepository, EnrollmentService enrollmentService, EnrollmentRepository enrollmentRepository, UserValidator userValidator, TransactionService transactionService, GroupValidator groupValidator) { super(repository, mapper, validator); this.studentRepository = studentRepository; this.enrollmentService = enrollmentService; this.enrollmentRepository = enrollmentRepository; this.userValidator = userValidator; this.transactionService = transactionService; + this.groupValidator = groupValidator; } private String wrapSearch(String search) { @@ -97,14 +101,23 @@ public void createGroupInvoice(String groupId, BigDecimal monthlyFee) { if (monthlyFee == null || monthlyFee.compareTo(BigDecimal.ZERO) <= 0) { throw new RestException(ErrorType.INVALID_INPUT, ErrorCodes.BadRequest); } - + Group group = groupValidator.validateIdAndGet(groupId); + String organizationId = userValidator.authenticateAndGetOrganizationId(); + if (!group.getOrganizationId().equals(organizationId)) { + throw new RestException(ErrorType.FORBIDDEN, ErrorCodes.Forbidden); + } + String levelName = group.getLevel().getName(); + boolean alreadyCreated = repository.checkIfAlreadyCreated(groupId, levelName, group.getCurrentMonth()); + if (alreadyCreated){ + throw new RestException(ErrorType.INVOICE_ALREADY_CREATED,ErrorCodes.AlreadyExists); + } List enrollments = enrollmentRepository.getAllByGroupId(groupId); if (enrollments.isEmpty()) { throw new RestException(ErrorType.ENROLLMENT_NOT_FOUND, ErrorCodes.NotFound); } List invoices = enrollments.stream() - .map(e -> mapper.toEntity(new InvoiceCreateDto(e, monthlyFee))) + .map(e -> mapper.toEntity(new InvoiceCreateDto(e.getId(), monthlyFee, levelName, group.getCurrentMonth()))) .toList(); repository.saveAll(invoices); diff --git a/src/main/java/org/example/crm/service/LessonService.java b/src/main/java/org/example/crm/service/LessonService.java index 49f6b28..717f9ce 100644 --- a/src/main/java/org/example/crm/service/LessonService.java +++ b/src/main/java/org/example/crm/service/LessonService.java @@ -4,7 +4,6 @@ import org.example.crm.entity.dto.lesson.LessonCreateDto; import org.example.crm.entity.dto.lesson.LessonDto; import org.example.crm.entity.dto.lesson.LessonUpdateDto; -import org.example.crm.entity.enums.EnrollmentPaymentStatus; import org.example.crm.entity.enums.GroupStatus; import org.example.crm.entity.model.*; import org.example.crm.eventListeners.GroupCycleCompletedEvent; @@ -18,9 +17,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; -import java.math.BigDecimal; -import java.util.List; - @Service public class LessonService extends AbstractService< LessonRepository, @@ -47,14 +43,14 @@ protected LessonService(LessonRepository repository, LessonMapper mapper, Lesson @Override public Page getAll(Pageable pageable, String search) { String organizationId = userValidator.authenticateAndGetOrganizationId(); - Page all = repository.findAll(pageable,organizationId, search); + Page all = repository.findAll(pageable, organizationId, search); return all.map(mapper::toDto); } @Override public LessonDto get(String id) { String organizationId = userValidator.authenticateAndGetOrganizationId(); - Lesson lesson = validator.validateIdAndGet(id,organizationId); + Lesson lesson = validator.validateIdAndGet(id, organizationId); return mapper.toDto(lesson); } @@ -65,19 +61,19 @@ public LessonDto create(LessonCreateDto createDto) { Group group = groupValidator.validateIdAndGet(createDto.groupId()); Level level = group.getLevel(); Integer lessonsInCurrMonth = repository.findLessonCountByGroupId(group.getId(), level.getName()).orElse(0) + 1; - Lesson entity = toEntity(createDto,String.format("%s.%s",group.getCurrentMonth(),lessonsInCurrMonth),group); + Lesson entity = toEntity(createDto, String.format("%s.%s", group.getCurrentMonth(), lessonsInCurrMonth), group); Lesson save = repository.save(entity); - group.registerCompletedLesson(lessonsInCurrMonth, groupLevelRepository); - groupRepository.save(group); if (!group.getStatus().equals(GroupStatus.COMPLETED)) { if (lessonsInCurrMonth == level.getLessonCount() / level.getDurationInMonths()) { eventPublisher.publishEvent(new GroupCycleCompletedEvent(group.getId(), level.getMonthlyFee())); } } + group.registerCompletedLesson(lessonsInCurrMonth, groupLevelRepository); + groupRepository.save(group); return mapper.toDto(save); } - private Lesson toEntity(LessonCreateDto createDto, String title,Group group) { + private Lesson toEntity(LessonCreateDto createDto, String title, Group group) { return new Lesson( title, createDto.topic(), @@ -92,7 +88,7 @@ private Lesson toEntity(LessonCreateDto createDto, String title,Group group) { @Override public LessonDto update(LessonUpdateDto updateDto, String id) { String organizationId = userValidator.authenticateAndGetOrganizationId(); - Lesson lesson = validator.validateIdAndGet(id,organizationId); + Lesson lesson = validator.validateIdAndGet(id, organizationId); mapper.mapUpdate(lesson, updateDto); Lesson save = repository.save(lesson); return mapper.toDto(save); @@ -101,7 +97,7 @@ public LessonDto update(LessonUpdateDto updateDto, String id) { @Override public void delete(String id) { String organizationId = userValidator.authenticateAndGetOrganizationId(); - Lesson lesson = validator.validateIdAndGet(id,organizationId); + Lesson lesson = validator.validateIdAndGet(id, organizationId); lesson.setDeleted(true); repository.save(lesson); } @@ -114,6 +110,6 @@ public Long getAllCount() { public Integer getLessonCountByGroup(String groupId, String name) { groupValidator.validateIdAndGet(groupId); - return repository.findLessonCountByGroupId(groupId,name).orElse(0); + return repository.findLessonCountByGroupId(groupId, name).orElse(0); } } diff --git a/src/main/java/org/example/crm/validator/EnrollmentValidator.java b/src/main/java/org/example/crm/validator/EnrollmentValidator.java index 918114f..a16129b 100644 --- a/src/main/java/org/example/crm/validator/EnrollmentValidator.java +++ b/src/main/java/org/example/crm/validator/EnrollmentValidator.java @@ -12,6 +12,7 @@ @AllArgsConstructor public class EnrollmentValidator { private final EnrollmentRepository repository; + private final UserValidator userValidator; public void validateId(String id) { Boolean exists = repository.checkId(id).orElse(false); @@ -21,7 +22,12 @@ public void validateId(String id) { } public Enrollment validateIdAndGet(String id) { - return repository.findById(id) + Enrollment enrollment = repository.findById(id) .orElseThrow(() -> new RestException(ErrorType.ENROLLMENT_NOT_FOUND, ErrorCodes.NotFound)); + String organizationId = userValidator.authenticateAndGetOrganizationId(); + if (!enrollment.getOrganizationId().equals(organizationId)){ + throw new RestException(ErrorType.FORBIDDEN,ErrorCodes.Forbidden); + } + return enrollment; } } diff --git a/src/main/java/org/example/crm/validator/GroupLevelValidator.java b/src/main/java/org/example/crm/validator/GroupLevelValidator.java index 2e2471d..95a4fe9 100644 --- a/src/main/java/org/example/crm/validator/GroupLevelValidator.java +++ b/src/main/java/org/example/crm/validator/GroupLevelValidator.java @@ -1,5 +1,6 @@ package org.example.crm.validator; +import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.example.crm.config.CustomUserDetails; import org.example.crm.entity.dto.groupLevel.GroupLevelCreateDto; @@ -15,23 +16,23 @@ @Component @RequiredArgsConstructor public class GroupLevelValidator { - final GroupLevelRepository groupRepository; + final GroupLevelRepository repository; public Level validateAndGet(String id,String organizationId) { - return groupRepository.findById(id,organizationId) + return repository.findById(id,organizationId) .orElseThrow(() -> new RestException(ErrorType.GROUP_LEVEL_NOT_FOUND, ErrorCodes.NotFound)); } public void validateForCreate(GroupLevelCreateDto createDto) { String organizationId = authenticateAndGetOrganizationId(); - Integer maxOrderNumber = groupRepository.getMaxOrderNumberByOrganizationId(organizationId); + Integer maxOrderNumber = repository.getMaxOrderNumberByOrganizationId(organizationId); if (createDto.orderNumber() == null || (maxOrderNumber != null && createDto.orderNumber() <= maxOrderNumber)) { throw new RestException(ErrorType.GROUP_LEVEL_ORDER_ALREADY_EXISTS, ErrorCodes.BadRequest); } - if (groupRepository.existsByOrganizationIdAndNameIgnoreCase(organizationId, createDto.name())) { + if (repository.existsByOrganizationIdAndNameIgnoreCase(organizationId, createDto.name())) { throw new RestException(ErrorType.GROUP_LEVEL_NAME_ALREADY_EXISTS, ErrorCodes.AlreadyExists); } @@ -52,4 +53,10 @@ private String authenticateAndGetOrganizationId() { } throw new RestException(ErrorType.UNAUTHORIZED, ErrorCodes.Unauthorized); } + + public String validateIdAndGetName(@NotNull String levelId) { + String organizationId = authenticateAndGetOrganizationId(); + return repository.checkAndGetName(levelId, organizationId) + .orElseThrow(() -> new RestException(ErrorType.GROUP_LEVEL_NOT_FOUND, ErrorCodes.NotFound)); + } } From e74aeee1367f713358f892fabe0991233f7171ce Mon Sep 17 00:00:00 2001 From: developer Date: Wed, 9 Sep 2026 15:38:27 +0500 Subject: [PATCH 2/2] invoice fix --- .../crm/controller/InvoiceController.java | 7 ++++++- .../crm/controller/StudentController.java | 4 ++-- .../example/crm/entity/dto/InvoiceDto.java | 4 +++- .../entity/dto/branch/BranchCreateDto.java | 2 -- .../crm/entity/dto/branch/BranchDto.java | 6 ------ .../entity/dto/branch/BranchUpdateDto.java | 3 --- .../entity/dto/enrollment/EnrollmentDto.java | 1 + .../crm/entity/dto/student/StudentDto.java | 10 +--------- .../eventListeners/InvoiceEventListener.java | 2 +- .../crm/initializer/DataInitializer.java | 19 ------------------ .../example/crm/mapper/EnrollmentMapper.java | 1 + .../org/example/crm/mapper/InvoiceMapper.java | 8 +++++--- .../crm/projection/InvoiceProjection.java | 10 +++------- .../crm/repository/GroupLevelRepository.java | 15 -------------- .../crm/repository/InvoiceRepository.java | 14 +++++-------- .../crm/repository/StudentRepository.java | 7 ------- .../crm/service/EnrollmentService.java | 2 +- .../example/crm/service/InvoiceService.java | 5 +++-- .../example/crm/service/StudentService.java | 20 +++++-------------- 19 files changed, 37 insertions(+), 103 deletions(-) diff --git a/src/main/java/org/example/crm/controller/InvoiceController.java b/src/main/java/org/example/crm/controller/InvoiceController.java index 6dc5ff0..f2402eb 100644 --- a/src/main/java/org/example/crm/controller/InvoiceController.java +++ b/src/main/java/org/example/crm/controller/InvoiceController.java @@ -3,7 +3,6 @@ import lombok.RequiredArgsConstructor; import org.example.crm.entity.dto.InvoiceCreateDto; import org.example.crm.entity.dto.InvoiceDto; -import org.example.crm.entity.dto.InvoiceUpdateDto; import org.example.crm.entity.enums.InvoiceStatus; import org.example.crm.service.InvoiceService; import org.springframework.data.domain.Page; @@ -44,6 +43,12 @@ public ResponseEntity createInvoice(@RequestBody InvoiceCreateDto cr return ResponseEntity.status(HttpStatus.CREATED).body(service.create(createDto)); } + @PostMapping("/{groupId}") + public ResponseEntity createGroupInvoice(@PathVariable String groupId){ + service.createGroupInvoice(groupId); + return ResponseEntity.noContent().build(); + } + @DeleteMapping("/{id}") public ResponseEntity deleteInvoice(@PathVariable String id) { service.delete(id); diff --git a/src/main/java/org/example/crm/controller/StudentController.java b/src/main/java/org/example/crm/controller/StudentController.java index bd2349f..8049f3a 100644 --- a/src/main/java/org/example/crm/controller/StudentController.java +++ b/src/main/java/org/example/crm/controller/StudentController.java @@ -85,8 +85,8 @@ public ResponseEntity delete(@PathVariable String id) { } @GetMapping("/me") - public ResponseEntity getMe(@RequestParam String groupId) { - StudentDto student = studentService.getMe(groupId); + public ResponseEntity getMe() { + StudentDto student = studentService.getMe(); return ResponseEntity.ok(student); } } \ No newline at end of file diff --git a/src/main/java/org/example/crm/entity/dto/InvoiceDto.java b/src/main/java/org/example/crm/entity/dto/InvoiceDto.java index 396225b..7598e79 100644 --- a/src/main/java/org/example/crm/entity/dto/InvoiceDto.java +++ b/src/main/java/org/example/crm/entity/dto/InvoiceDto.java @@ -1,6 +1,7 @@ package org.example.crm.entity.dto; import org.example.crm.entity.dto.enrollment.EnrollmentDto; +import org.example.crm.entity.enums.InvoiceStatus; import java.math.BigDecimal; import java.time.LocalDateTime; @@ -10,6 +11,7 @@ public record InvoiceDto( String invoiceNumber, BigDecimal amount, LocalDateTime issuedAt, - EnrollmentDto enrollmentDto + EnrollmentDto enrollmentDto, + InvoiceStatus paymentStatus ) { } diff --git a/src/main/java/org/example/crm/entity/dto/branch/BranchCreateDto.java b/src/main/java/org/example/crm/entity/dto/branch/BranchCreateDto.java index 1e9b731..f925b40 100644 --- a/src/main/java/org/example/crm/entity/dto/branch/BranchCreateDto.java +++ b/src/main/java/org/example/crm/entity/dto/branch/BranchCreateDto.java @@ -1,9 +1,7 @@ package org.example.crm.entity.dto.branch; -import java.math.BigDecimal; public record BranchCreateDto( - BigDecimal chargeForMonth, String name, String address, String googlePlaceId, diff --git a/src/main/java/org/example/crm/entity/dto/branch/BranchDto.java b/src/main/java/org/example/crm/entity/dto/branch/BranchDto.java index 6dc6354..f97e54f 100644 --- a/src/main/java/org/example/crm/entity/dto/branch/BranchDto.java +++ b/src/main/java/org/example/crm/entity/dto/branch/BranchDto.java @@ -1,13 +1,7 @@ package org.example.crm.entity.dto.branch; -import org.example.crm.entity.dto.organization.OrganizationDto; - -import java.math.BigDecimal; - public record BranchDto( - OrganizationDto organization, String id, - BigDecimal chargeForMonth, String name, String address, String googlePlaceId, diff --git a/src/main/java/org/example/crm/entity/dto/branch/BranchUpdateDto.java b/src/main/java/org/example/crm/entity/dto/branch/BranchUpdateDto.java index 1399bb3..1594944 100644 --- a/src/main/java/org/example/crm/entity/dto/branch/BranchUpdateDto.java +++ b/src/main/java/org/example/crm/entity/dto/branch/BranchUpdateDto.java @@ -1,9 +1,6 @@ package org.example.crm.entity.dto.branch; -import java.math.BigDecimal; - public record BranchUpdateDto( - BigDecimal chargeForMonth, String name, String address, String googlePlaceId, diff --git a/src/main/java/org/example/crm/entity/dto/enrollment/EnrollmentDto.java b/src/main/java/org/example/crm/entity/dto/enrollment/EnrollmentDto.java index b9e050e..7e10e39 100644 --- a/src/main/java/org/example/crm/entity/dto/enrollment/EnrollmentDto.java +++ b/src/main/java/org/example/crm/entity/dto/enrollment/EnrollmentDto.java @@ -3,6 +3,7 @@ public record EnrollmentDto( String id, String studentId, + String studentFullName, String groupId, String reason) { } diff --git a/src/main/java/org/example/crm/entity/dto/student/StudentDto.java b/src/main/java/org/example/crm/entity/dto/student/StudentDto.java index 5a42bc8..44fad55 100644 --- a/src/main/java/org/example/crm/entity/dto/student/StudentDto.java +++ b/src/main/java/org/example/crm/entity/dto/student/StudentDto.java @@ -1,19 +1,11 @@ package org.example.crm.entity.dto.student; -import io.swagger.v3.oas.annotations.media.Schema; import org.example.crm.entity.dto.user.UserDto; -import org.example.crm.entity.enums.EnrollmentPaymentStatus; import java.math.BigDecimal; public record StudentDto(String id, UserDto userDto, String parentPhone, - BigDecimal balance, - @Schema( - description = "Student payment status", - allowableValues = {"UNPAID", "PARTIAL", "PAID"} - ) - EnrollmentPaymentStatus status - ) { + BigDecimal balance){ } diff --git a/src/main/java/org/example/crm/eventListeners/InvoiceEventListener.java b/src/main/java/org/example/crm/eventListeners/InvoiceEventListener.java index 1c3e740..46f6c2e 100644 --- a/src/main/java/org/example/crm/eventListeners/InvoiceEventListener.java +++ b/src/main/java/org/example/crm/eventListeners/InvoiceEventListener.java @@ -16,6 +16,6 @@ public class InvoiceEventListener { @Async @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) public void handleGroupBilling(GroupCycleCompletedEvent event) { - invoiceService.createGroupInvoice(event.groupId(), event.monthlyFee()); + invoiceService.createGroupInvoice(event.groupId()); } } \ No newline at end of file diff --git a/src/main/java/org/example/crm/initializer/DataInitializer.java b/src/main/java/org/example/crm/initializer/DataInitializer.java index ba76349..1616520 100644 --- a/src/main/java/org/example/crm/initializer/DataInitializer.java +++ b/src/main/java/org/example/crm/initializer/DataInitializer.java @@ -9,10 +9,8 @@ import org.springframework.boot.CommandLineRunner; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; - import java.math.BigDecimal; import java.time.LocalDate; -import java.time.LocalDateTime; import java.time.LocalTime; @Component @@ -193,34 +191,21 @@ public void run(String... args) { Enrollment enrollment1 = new Enrollment(); enrollment1.setStudent(student1); enrollment1.setGroup(group1); - enrollment1.setStatus(EnrollmentPaymentStatus.PARTIAL); - enrollment1.setMonthlyFee(group1.getLevel().getMonthlyFee()); - enrollment1.setPaidAmount(group1.getLevel().getMonthlyFee().subtract(BigDecimal.valueOf(100))); entityManager.persist(enrollment1); Enrollment enrollment2 = new Enrollment(); enrollment2.setStudent(student2); enrollment2.setGroup(group1); - enrollment2.setStatus(EnrollmentPaymentStatus.UNPAID); - enrollment2.setMonthlyFee(group1.getLevel().getMonthlyFee()); - enrollment2.setPaidAmount(BigDecimal.ZERO); entityManager.persist(enrollment2); Enrollment enrollment3 = new Enrollment(); enrollment3.setStudent(student3); enrollment3.setGroup(group2); - enrollment3.setStatus(EnrollmentPaymentStatus.PAID); - enrollment3.setMonthlyFee(group2.getLevel().getMonthlyFee()); - enrollment3.setPaidAmount(group2.getLevel().getMonthlyFee()); entityManager.persist(enrollment3); Enrollment enrollment4 = new Enrollment(); enrollment4.setStudent(student4); enrollment4.setGroup(group2); - - enrollment4.setStatus(EnrollmentPaymentStatus.PAID); - enrollment4.setMonthlyFee(group2.getLevel().getMonthlyFee()); - enrollment4.setPaidAmount(group2.getLevel().getMonthlyFee()); entityManager.persist(enrollment4); // ============ INVOICES ============ @@ -228,13 +213,11 @@ public void run(String... args) { invoice1.setInvoiceNumber("INV-001"); invoice1.setAmount(new BigDecimal("150.00")); invoice1.setPaymentStatus(InvoiceStatus.PAID); - invoice1.setIssuedAt(LocalDateTime.now().minusMonths(2)); invoiceRepository.save(invoice1); Invoice invoice2 = new Invoice(); invoice2.setInvoiceNumber("INV-002"); invoice2.setAmount(new BigDecimal("150.00")); - invoice2.setIssuedAt(LocalDateTime.now().minusDays(5)); invoice2.setEnrollment(enrollment1); invoiceRepository.save(invoice2); @@ -242,14 +225,12 @@ public void run(String... args) { invoice3.setInvoiceNumber("INV-003"); invoice3.setAmount(new BigDecimal("200.00")); invoice3.setPaymentStatus(InvoiceStatus.OVERDUE); - invoice3.setIssuedAt(LocalDateTime.now().minusMonths(1)); invoice3.setEnrollment(enrollment3); invoiceRepository.save(invoice3); Invoice invoice4 = new Invoice(); invoice4.setInvoiceNumber("INV-004"); invoice4.setAmount(new BigDecimal("200.00")); - invoice4.setIssuedAt(LocalDateTime.now().minusDays(3)); invoice4.setEnrollment(enrollment4); invoiceRepository.save(invoice4); diff --git a/src/main/java/org/example/crm/mapper/EnrollmentMapper.java b/src/main/java/org/example/crm/mapper/EnrollmentMapper.java index 006ad4e..7575599 100644 --- a/src/main/java/org/example/crm/mapper/EnrollmentMapper.java +++ b/src/main/java/org/example/crm/mapper/EnrollmentMapper.java @@ -10,5 +10,6 @@ public interface EnrollmentMapper { @Mapping(source = "student.id",target = "studentId") @Mapping(source = "group.id",target = "groupId") + @Mapping(source = "student.user.fullName",target = "studentFullName") EnrollmentDto toDto(Enrollment enrollment); } diff --git a/src/main/java/org/example/crm/mapper/InvoiceMapper.java b/src/main/java/org/example/crm/mapper/InvoiceMapper.java index 945c0d0..0c8f82a 100644 --- a/src/main/java/org/example/crm/mapper/InvoiceMapper.java +++ b/src/main/java/org/example/crm/mapper/InvoiceMapper.java @@ -40,8 +40,8 @@ public InvoiceDto toDto(Invoice invoice) { invoice.getInvoiceNumber(), invoice.getAmount(), invoice.getCreatedAt(), - invoice.getEnrollment() != null ? enrollmentMapper.toDto(invoice.getEnrollment()) : null - + invoice.getEnrollment() != null ? enrollmentMapper.toDto(invoice.getEnrollment()) : null, + invoice.getPaymentStatus() ); } @@ -54,9 +54,11 @@ public InvoiceDto toDtoFromProjection(InvoiceProjection projection) { new EnrollmentDto( projection.getEnrollmentId(), projection.getStudentId(), + projection.getStudentFullName(), projection.getGroupId(), projection.getReason() - ) + ), + projection.getPaymentStatus() ); } diff --git a/src/main/java/org/example/crm/projection/InvoiceProjection.java b/src/main/java/org/example/crm/projection/InvoiceProjection.java index 9ea645b..ddb5b2a 100644 --- a/src/main/java/org/example/crm/projection/InvoiceProjection.java +++ b/src/main/java/org/example/crm/projection/InvoiceProjection.java @@ -1,23 +1,19 @@ package org.example.crm.projection; -import org.example.crm.entity.enums.*; +import org.example.crm.entity.enums.InvoiceStatus; import java.math.BigDecimal; -import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.LocalTime; public interface InvoiceProjection { String getId(); String getInvoiceNumber(); String getEnrollmentId(); String getStudentId(); + String getStudentFullName(); String getGroupId(); String getReason(); - BigDecimal getMonthlyFee(); - BigDecimal getPaidAmount(); - EnrollmentPaymentStatus getEnrollmentStatus(); - BigDecimal getAmount(); LocalDateTime getIssuedAt(); + InvoiceStatus getPaymentStatus(); } diff --git a/src/main/java/org/example/crm/repository/GroupLevelRepository.java b/src/main/java/org/example/crm/repository/GroupLevelRepository.java index 1c2c906..5f55329 100644 --- a/src/main/java/org/example/crm/repository/GroupLevelRepository.java +++ b/src/main/java/org/example/crm/repository/GroupLevelRepository.java @@ -1,6 +1,5 @@ package org.example.crm.repository; -import jakarta.validation.constraints.NotNull; import org.example.crm.entity.model.Level; import org.example.crm.projection.LevelNamesProjection; import org.springframework.data.jpa.repository.JpaRepository; @@ -33,20 +32,6 @@ public interface GroupLevelRepository extends JpaRepository { Optional findLevelByIdAndOrganizationId(String id, String organizationId); - @Query(""" - select case when count(l) > 0 then true else false end - from Level l - where l.id = :id - and l.orderNumber in ( - select l2.orderNumber - from Level l2 - where l2.organizationId = l.organizationId - and l2.deleted = false - ) - and l.deleted = false -""") - boolean checkLevelOrder(String id); - @Query(""" select max(l.orderNumber) from Level l diff --git a/src/main/java/org/example/crm/repository/InvoiceRepository.java b/src/main/java/org/example/crm/repository/InvoiceRepository.java index 71ac243..c1fb0f2 100644 --- a/src/main/java/org/example/crm/repository/InvoiceRepository.java +++ b/src/main/java/org/example/crm/repository/InvoiceRepository.java @@ -23,18 +23,14 @@ public interface InvoiceRepository extends JpaRepository { select i.id as id, i.invoiceNumber as invoiceNumber, - e.id as enrollmentId, s.id as studentId, + su.fullName as fullName, g.id as groupId, e.leavingReason as reason, - e.monthlyFee as monthlyFee, - e.paidAmount as paidAmount, - e.status as enrollmentStatus, - i.amount as amount, - i.issuedAt as issuedAt, - i.paymentStatus as status + i.createdAt as issuedAt, + i.paymentStatus as paymentStatus from Invoice i join i.enrollment e join e.student s @@ -49,8 +45,8 @@ public interface InvoiceRepository extends JpaRepository { or su.phone ilike :search or g.name ilike :search or i.invoiceNumber ilike :search ) - and (i.issuedAt >= :from) - and (i.issuedAt <= :to) + and (i.createdAt >= :from) + and (i.createdAt <= :to) and (:status is null or i.paymentStatus = :status) """) Page getAllInvoicesByFilter( diff --git a/src/main/java/org/example/crm/repository/StudentRepository.java b/src/main/java/org/example/crm/repository/StudentRepository.java index 93d69e5..c0d9b0e 100644 --- a/src/main/java/org/example/crm/repository/StudentRepository.java +++ b/src/main/java/org/example/crm/repository/StudentRepository.java @@ -20,13 +20,6 @@ import java.util.Optional; public interface StudentRepository extends JpaRepository { - @Query(""" - SELECT s FROM Student s - JOIN s.user u - WHERE u.deleted = false - AND (:search IS NULL OR :search = '' OR LOWER(u.fullName) LIKE LOWER(CONCAT('%', :search, '%'))) - """) - Page searchStudents(@Param("search") String search, Pageable pageable); @Query("SELECT s FROM Student s WHERE s.id IN " + diff --git a/src/main/java/org/example/crm/service/EnrollmentService.java b/src/main/java/org/example/crm/service/EnrollmentService.java index 1b29ebd..15e670f 100644 --- a/src/main/java/org/example/crm/service/EnrollmentService.java +++ b/src/main/java/org/example/crm/service/EnrollmentService.java @@ -69,7 +69,7 @@ public EnrollmentDto create(EnrollmentCreateDto createDto) { Student student = studentValidator.validateIdAndGet(createDto.studentId()); Group group = groupValidator.validateIdAndGet(createDto.groupId()); studentRepository.save(student); - Enrollment enrollment = new Enrollment(student, group, null, group.getLevel().getMonthlyFee(), BigDecimal.ZERO, EnrollmentPaymentStatus.UNPAID); + Enrollment enrollment = new Enrollment(student, group, null); return mapper.toDto(repository.save(enrollment)); } diff --git a/src/main/java/org/example/crm/service/InvoiceService.java b/src/main/java/org/example/crm/service/InvoiceService.java index 84e32aa..4ff6083 100644 --- a/src/main/java/org/example/crm/service/InvoiceService.java +++ b/src/main/java/org/example/crm/service/InvoiceService.java @@ -97,11 +97,12 @@ public Page getAllInvoices(String search, LocalDateTime from, LocalD @Transactional - public void createGroupInvoice(String groupId, BigDecimal monthlyFee) { + public void createGroupInvoice(String groupId) { + Group group = groupValidator.validateIdAndGet(groupId); + BigDecimal monthlyFee = group.getLevel().getMonthlyFee(); if (monthlyFee == null || monthlyFee.compareTo(BigDecimal.ZERO) <= 0) { throw new RestException(ErrorType.INVALID_INPUT, ErrorCodes.BadRequest); } - Group group = groupValidator.validateIdAndGet(groupId); String organizationId = userValidator.authenticateAndGetOrganizationId(); if (!group.getOrganizationId().equals(organizationId)) { throw new RestException(ErrorType.FORBIDDEN, ErrorCodes.Forbidden); diff --git a/src/main/java/org/example/crm/service/StudentService.java b/src/main/java/org/example/crm/service/StudentService.java index 0b82ba4..bf48128 100644 --- a/src/main/java/org/example/crm/service/StudentService.java +++ b/src/main/java/org/example/crm/service/StudentService.java @@ -84,22 +84,16 @@ public StudentDto update(StudentUpdateDto updateDto, String id) { @Override public void delete(String id) { - - validator.validateId(id); repository.softDelete(id); } public Long getAllCount() { - - String organizationId = userValidator.authenticateAndGetOrganizationId(); return repository.countStudentsByOrganizationId(organizationId); } public List getStudentsByGroupId(String groupId) { - - List studentByGroupId = repository.getStudentShowByGroupId(groupId); return studentByGroupId .stream() @@ -115,23 +109,19 @@ public List getByPhone(String phone) { .toList(); } - public StudentDto getMe(String groupId) { - User userId = userValidator.authenticateAndGetUser(); - Student student = validator.validateStudentByUserId(userId.getId()); - Enrollment enrollment = enrollmentRepository.findByStudentIdAndGroupId(student.getId(), groupId) - .orElseThrow(() -> new RestException(ErrorType.ENROLLMENT_NOT_FOUND, ErrorCodes.NotFound)); + public StudentDto getMe() { + User user = userValidator.authenticateAndGetUser(); + Student student = validator.validateStudentByUserId(user.getId()); return new StudentDto( student.getId(), userMapper.toDto(student.getUser()), student.getParentPhone(), - enrollment.getPaidAmount().subtract(enrollment.getMonthlyFee()), - enrollment.getStatus() + student.getBalance() ); } public Invoice getLatestInvoice(@NotNull String studentId) { - Invoice invoice = repository.findLatestInvoiceByStudentId(studentId) + return repository.findLatestInvoiceByStudentId(studentId) .orElseThrow(() -> new RestException(ErrorType.INVOICE_NOT_FOUND, ErrorCodes.NotFound)); - return invoice; } }