Skip to content
Merged

N #31

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -44,6 +43,12 @@ public ResponseEntity<InvoiceDto> createInvoice(@RequestBody InvoiceCreateDto cr
return ResponseEntity.status(HttpStatus.CREATED).body(service.create(createDto));
}

@PostMapping("/{groupId}")
public ResponseEntity<Void> createGroupInvoice(@PathVariable String groupId){
service.createGroupInvoice(groupId);
return ResponseEntity.noContent().build();
}

@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteInvoice(@PathVariable String id) {
service.delete(id);
Expand Down
Original file line number Diff line number Diff line change
@@ -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
) {
}
4 changes: 3 additions & 1 deletion src/main/java/org/example/crm/entity/dto/InvoiceDto.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -10,6 +11,7 @@ public record InvoiceDto(
String invoiceNumber,
BigDecimal amount,
LocalDateTime issuedAt,
EnrollmentDto enrollmentDto
EnrollmentDto enrollmentDto,
InvoiceStatus paymentStatus
) {
}
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
public record EnrollmentDto(
String id,
String studentId,
String studentFullName,
String groupId,
String reason) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/example/crm/entity/model/Invoice.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,7 @@ public class Invoice extends BaseEntity {
@ManyToOne(fetch = FetchType.LAZY, optional = false)
private Enrollment enrollment;

private String level;

private Integer month;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
3 changes: 2 additions & 1 deletion src/main/java/org/example/crm/exceptions/ErrorType.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/main/java/org/example/crm/mapper/EnrollmentMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
16 changes: 12 additions & 4 deletions src/main/java/org/example/crm/mapper/InvoiceMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,26 @@
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
@RequiredArgsConstructor
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;
}
Expand All @@ -34,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()
);
}

Expand All @@ -48,9 +54,11 @@ public InvoiceDto toDtoFromProjection(InvoiceProjection projection) {
new EnrollmentDto(
projection.getEnrollmentId(),
projection.getStudentId(),
projection.getStudentFullName(),
projection.getGroupId(),
projection.getReason()
)
),
projection.getPaymentStatus()
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
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();

Expand Down
17 changes: 3 additions & 14 deletions src/main/java/org/example/crm/repository/GroupLevelRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,6 @@ public interface GroupLevelRepository extends JpaRepository<Level, String> {
Optional<Level> 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
Expand Down Expand Up @@ -99,4 +85,7 @@ Optional<Level> getFirstLevelForGroup(

@Query("select l from Level l where l.organizationId=:orgId and l.id=:id and l.deleted=false ")
Optional<Level> 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<String> checkAndGetName(@Param("id") String levelId,@Param("orgId")String organizationId);
}
12 changes: 11 additions & 1 deletion src/main/java/org/example/crm/repository/InvoiceRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ public interface InvoiceRepository extends JpaRepository<Invoice, String> {
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,
i.amount as amount,
Expand Down Expand Up @@ -94,4 +94,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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,6 @@
import java.util.Optional;

public interface StudentRepository extends JpaRepository<Student, String> {
@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<StudentProjection> searchStudents(@Param("search") String search, Pageable pageable);


@Query("SELECT s FROM Student s WHERE s.id IN " +
Expand Down
22 changes: 18 additions & 4 deletions src/main/java/org/example/crm/service/InvoiceService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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) {
Expand Down Expand Up @@ -93,18 +97,28 @@ public Page<InvoiceDto> 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);
}

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<Enrollment> enrollments = enrollmentRepository.getAllByGroupId(groupId);
if (enrollments.isEmpty()) {
throw new RestException(ErrorType.ENROLLMENT_NOT_FOUND, ErrorCodes.NotFound);
}

List<Invoice> 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);

Expand Down
Loading