| @@ -8,7 +8,7 @@ import java.util.List; | |||||
| import java.util.Optional; | import java.util.Optional; | ||||
| public interface CompanyRepository extends AbstractRepository<Company, Long> { | public interface CompanyRepository extends AbstractRepository<Company, Long> { | ||||
| List<CompanySearchInfo> findCompanySearchInfoBy(); | |||||
| List<CompanySearchInfo> findCompanySearchInfoByAndDeletedFalse(); | |||||
| Optional<Company> findCompanyByIdAndDeletedFalse(Long companyId); | Optional<Company> findCompanyByIdAndDeletedFalse(Long companyId); | ||||
| } | } | ||||
| @@ -1,10 +1,14 @@ | |||||
| package com.ffii.tsms.modules.data.entity; | package com.ffii.tsms.modules.data.entity; | ||||
| import com.fasterxml.jackson.annotation.JsonManagedReference; | |||||
| import com.ffii.core.entity.BaseEntity; | import com.ffii.core.entity.BaseEntity; | ||||
| import jakarta.persistence.*; | import jakarta.persistence.*; | ||||
| import jakarta.validation.constraints.NotNull; | import jakarta.validation.constraints.NotNull; | ||||
| import java.time.LocalTime; | import java.time.LocalTime; | ||||
| import java.util.List; | |||||
| import static jakarta.persistence.CascadeType.ALL; | |||||
| @Entity | @Entity | ||||
| @Table(name = "subsidiary") | @Table(name = "subsidiary") | ||||
| @@ -31,6 +35,10 @@ public class Subsidiary extends BaseEntity<Long> { | |||||
| @JoinColumn(name = "typeId") | @JoinColumn(name = "typeId") | ||||
| private SubsidiaryType subsidiaryType; | private SubsidiaryType subsidiaryType; | ||||
| @OneToMany(mappedBy = "subsidiary", cascade = ALL, orphanRemoval = true) | |||||
| @JsonManagedReference | |||||
| private List<SubsidiaryContact> subsidiaryContacts; | |||||
| public String getAddress() { | public String getAddress() { | ||||
| return address; | return address; | ||||
| } | } | ||||
| @@ -78,4 +86,12 @@ public class Subsidiary extends BaseEntity<Long> { | |||||
| public void setSubsidiaryType(SubsidiaryType subsidiaryType) { | public void setSubsidiaryType(SubsidiaryType subsidiaryType) { | ||||
| this.subsidiaryType = subsidiaryType; | this.subsidiaryType = subsidiaryType; | ||||
| } | } | ||||
| public List<SubsidiaryContact> getSubsidiaryContacts() { | |||||
| return subsidiaryContacts; | |||||
| } | |||||
| public void setSubsidiaryContacts(List<SubsidiaryContact> subsidiaryContacts) { | |||||
| this.subsidiaryContacts = subsidiaryContacts; | |||||
| } | |||||
| } | } | ||||
| @@ -1,18 +1,17 @@ | |||||
| package com.ffii.tsms.modules.data.entity; | package com.ffii.tsms.modules.data.entity; | ||||
| import com.fasterxml.jackson.annotation.JsonBackReference; | |||||
| import com.fasterxml.jackson.annotation.JsonManagedReference; | |||||
| import com.ffii.core.entity.IdEntity; | import com.ffii.core.entity.IdEntity; | ||||
| import jakarta.persistence.Column; | |||||
| import jakarta.persistence.Entity; | |||||
| import jakarta.persistence.JoinColumn; | |||||
| import jakarta.persistence.OneToOne; | |||||
| import jakarta.persistence.Table; | |||||
| import jakarta.persistence.*; | |||||
| import jakarta.validation.constraints.NotNull; | import jakarta.validation.constraints.NotNull; | ||||
| @Entity | @Entity | ||||
| @Table(name = "subsidiary_contact") | @Table(name = "subsidiary_contact") | ||||
| public class SubsidiaryContact extends IdEntity<Long> { | public class SubsidiaryContact extends IdEntity<Long> { | ||||
| @NotNull | @NotNull | ||||
| @OneToOne | |||||
| @ManyToOne | |||||
| @JsonBackReference | |||||
| @JoinColumn(name = "subsidiaryId") | @JoinColumn(name = "subsidiaryId") | ||||
| private Subsidiary subsidiary; | private Subsidiary subsidiary; | ||||
| @@ -9,5 +9,7 @@ import java.util.Optional; | |||||
| public interface SubsidiaryRepository extends AbstractRepository<Subsidiary, Long> { | public interface SubsidiaryRepository extends AbstractRepository<Subsidiary, Long> { | ||||
| List<Subsidiary> findAllByDeletedFalse(); | List<Subsidiary> findAllByDeletedFalse(); | ||||
| List<Subsidiary> findAllByDeletedFalseAndIdIn(List<Long> id); | |||||
| Optional<Subsidiary> findByCode(@Param("code") String code); | Optional<Subsidiary> findByCode(@Param("code") String code); | ||||
| } | } | ||||
| @@ -22,4 +22,7 @@ interface StaffSearchInfo { | |||||
| @get:Value("#{target.currentPosition?.name}") | @get:Value("#{target.currentPosition?.name}") | ||||
| val currentPosition: String? | val currentPosition: String? | ||||
| @get:Value("#{target.user?.id}") | |||||
| val userId: Long? | |||||
| } | } | ||||
| @@ -18,7 +18,7 @@ open class CompanyService( | |||||
| private val jdbcDao: JdbcDao, | private val jdbcDao: JdbcDao, | ||||
| ) : AbstractBaseEntityService<Company, Long, CompanyRepository>(jdbcDao, companyRepository) { | ) : AbstractBaseEntityService<Company, Long, CompanyRepository>(jdbcDao, companyRepository) { | ||||
| open fun allCompanys(): List<CompanySearchInfo>{ | open fun allCompanys(): List<CompanySearchInfo>{ | ||||
| return companyRepository.findCompanySearchInfoBy() | |||||
| return companyRepository.findCompanySearchInfoByAndDeletedFalse() | |||||
| } | } | ||||
| open fun getCompanyDetails(companyId: Long): Company? { | open fun getCompanyDetails(companyId: Long): Company? { | ||||
| @@ -1,5 +1,6 @@ | |||||
| package com.ffii.tsms.modules.data.service | package com.ffii.tsms.modules.data.service | ||||
| import com.ffii.core.exception.UnprocessableEntityException | |||||
| import com.ffii.core.support.AbstractBaseEntityService | import com.ffii.core.support.AbstractBaseEntityService | ||||
| import com.ffii.core.support.JdbcDao | import com.ffii.core.support.JdbcDao | ||||
| import com.ffii.tsms.modules.common.SecurityUtils | import com.ffii.tsms.modules.common.SecurityUtils | ||||
| @@ -8,12 +9,10 @@ import com.ffii.tsms.modules.data.entity.projections.StaffSearchInfo | |||||
| import com.ffii.tsms.modules.data.web.models.NewStaffRequest | import com.ffii.tsms.modules.data.web.models.NewStaffRequest | ||||
| import com.ffii.tsms.modules.user.entity.User | import com.ffii.tsms.modules.user.entity.User | ||||
| import com.ffii.tsms.modules.user.entity.UserRepository | import com.ffii.tsms.modules.user.entity.UserRepository | ||||
| import org.springframework.data.jpa.domain.AbstractPersistable_.id | |||||
| import org.springframework.security.crypto.password.PasswordEncoder | import org.springframework.security.crypto.password.PasswordEncoder | ||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||
| import org.springframework.transaction.annotation.Transactional | import org.springframework.transaction.annotation.Transactional | ||||
| import java.util.* | import java.util.* | ||||
| import java.util.stream.Collectors | |||||
| import kotlin.jvm.optionals.getOrNull | import kotlin.jvm.optionals.getOrNull | ||||
| @@ -96,6 +95,13 @@ open class StaffsService( | |||||
| @Transactional(rollbackFor = [Exception::class]) | @Transactional(rollbackFor = [Exception::class]) | ||||
| open fun saveStaff(req: NewStaffRequest): Staff { | open fun saveStaff(req: NewStaffRequest): Staff { | ||||
| // if (req.staffId) | |||||
| val checkStaffIdList: List<StaffSearchInfo> = staffRepository.findStaffSearchInfoByAndDeletedFalse() | |||||
| checkStaffIdList.forEach{ s -> | |||||
| if (s.staffId == req.staffId) { | |||||
| throw UnprocessableEntityException("Duplicated StaffId Found") | |||||
| } | |||||
| } | |||||
| val currentPosition = positionRepository.findById(req.currentPositionId).orElseThrow() | val currentPosition = positionRepository.findById(req.currentPositionId).orElseThrow() | ||||
| val joinPosition = positionRepository.findById(req.joinPositionId).orElseThrow() | val joinPosition = positionRepository.findById(req.joinPositionId).orElseThrow() | ||||
| val company = companyRepository.findById(req.companyId).orElseThrow() | val company = companyRepository.findById(req.companyId).orElseThrow() | ||||
| @@ -39,7 +39,7 @@ open class TeamService( | |||||
| @Transactional(rollbackFor = [Exception::class]) | @Transactional(rollbackFor = [Exception::class]) | ||||
| open fun saveTeam(req: NewTeamRequest): Team { | open fun saveTeam(req: NewTeamRequest): Team { | ||||
| val ids = req.addStaffIds!! | val ids = req.addStaffIds!! | ||||
| println(ids) | |||||
| // println(ids) | |||||
| val teamLead = staffRepository.findById(ids[0]).orElseThrow() | val teamLead = staffRepository.findById(ids[0]).orElseThrow() | ||||
| val teamName = "Team " + teamLead.name | val teamName = "Team " + teamLead.name | ||||
| @@ -7,6 +7,7 @@ import com.ffii.tsms.modules.data.service.SkillService | |||||
| import com.ffii.tsms.modules.data.web.models.NewSkillRequest | import com.ffii.tsms.modules.data.web.models.NewSkillRequest | ||||
| import jakarta.servlet.http.HttpServletRequest | import jakarta.servlet.http.HttpServletRequest | ||||
| import jakarta.validation.Valid | import jakarta.validation.Valid | ||||
| import org.springframework.http.HttpStatus | |||||
| import org.springframework.web.bind.ServletRequestBindingException | import org.springframework.web.bind.ServletRequestBindingException | ||||
| import org.springframework.web.bind.annotation.* | import org.springframework.web.bind.annotation.* | ||||
| @@ -25,6 +26,11 @@ class SkillController(private val skillService: SkillService) { | |||||
| args["id"] = id | args["id"] = id | ||||
| return skillService.list(args); | return skillService.list(args); | ||||
| } | } | ||||
| @DeleteMapping("/delete/{id}") | |||||
| @ResponseStatus(HttpStatus.NO_CONTENT) | |||||
| fun delete(@PathVariable id: Long?) { | |||||
| skillService.markDelete(id) | |||||
| } | |||||
| @GetMapping | @GetMapping | ||||
| fun list(): List<Map<String, Any>> { | fun list(): List<Map<String, Any>> { | ||||
| @@ -5,10 +5,6 @@ import jakarta.validation.constraints.NotNull | |||||
| import java.time.LocalDate | import java.time.LocalDate | ||||
| data class NewStaffRequest( | data class NewStaffRequest( | ||||
| val id: Long?, | |||||
| // @field:NotNull(message = "Staff userId cannot be empty") | |||||
| // val userId: Long, | |||||
| @field:NotBlank(message = "Staff name cannot be empty") | @field:NotBlank(message = "Staff name cannot be empty") | ||||
| val name: String, | val name: String, | ||||
| @field:NotBlank(message = "Staff staffId cannot be empty") | @field:NotBlank(message = "Staff staffId cannot be empty") | ||||
| @@ -17,21 +13,30 @@ data class NewStaffRequest( | |||||
| val companyId: Long, | val companyId: Long, | ||||
| @field:NotNull(message = "Staff salaryId cannot be empty") | @field:NotNull(message = "Staff salaryId cannot be empty") | ||||
| val salaryId: Long, | val salaryId: Long, | ||||
| // @field:NotNull(message = "Staff skillSetId cannot be empty") | |||||
| val skillSetId: List<Long>?, | |||||
| @field:NotNull(message = "joinDate cannot be empty") | |||||
| val joinDate: LocalDate, | val joinDate: LocalDate, | ||||
| @field:NotNull(message = "Staff currentPositionId cannot be empty") | |||||
| val currentPositionId: Long, | val currentPositionId: Long, | ||||
| // val salaryEffId: Long, | |||||
| @field:NotNull(message = "Staff joinPositionId cannot be empty") | |||||
| val joinPositionId: Long, | val joinPositionId: Long, | ||||
| val gradeId: Long?, | |||||
| val teamId: Long?, | |||||
| @field:NotNull(message = "Staff departmentId cannot be empty") | |||||
| val departmentId: Long, | val departmentId: Long, | ||||
| @field:NotBlank(message = "Staff phone1 cannot be empty") | |||||
| val phone1: String, | val phone1: String, | ||||
| val phone2: String?, | |||||
| @field:NotBlank(message = "Staff email cannot be empty") | |||||
| val email: String, | val email: String, | ||||
| @field:NotBlank(message = "Staff emergContactName cannot be empty") | |||||
| val emergContactName: String, | val emergContactName: String, | ||||
| @field:NotBlank(message = "Staff emergContactPhone cannot be empty") | |||||
| val emergContactPhone: String, | val emergContactPhone: String, | ||||
| @field:NotBlank(message = "Staff employType cannot be empty") | |||||
| val employType: String, | val employType: String, | ||||
| val id: Long?, | |||||
| val skillSetId: List<Long>?, | |||||
| val gradeId: Long?, | |||||
| val phone2: String?, | |||||
| val teamId: Long?, | |||||
| val departDate: LocalDate?, | val departDate: LocalDate?, | ||||
| val departReason: String?, | val departReason: String?, | ||||
| val remark: String?, | val remark: String?, | ||||
| @@ -108,4 +108,14 @@ open class Project : BaseEntity<Long>() { | |||||
| inverseJoinColumns = [JoinColumn(name = "workNaturesId")] | inverseJoinColumns = [JoinColumn(name = "workNaturesId")] | ||||
| ) | ) | ||||
| open var workNatures: MutableSet<WorkNature> = mutableSetOf() | open var workNatures: MutableSet<WorkNature> = mutableSetOf() | ||||
| @ManyToOne | |||||
| @JoinColumn(name = "taskTemplateId") | |||||
| open var taskTemplate: TaskTemplate? = null | |||||
| @Column(name = "status") | |||||
| open var status: String? = null | |||||
| @Column(name = "isClpProject") | |||||
| open var isClpProject: Boolean? = null | |||||
| } | } | ||||
| @@ -4,6 +4,7 @@ import com.ffii.core.support.AbstractRepository | |||||
| import com.ffii.tsms.modules.project.entity.projections.InvoiceInfoSearchInfo | import com.ffii.tsms.modules.project.entity.projections.InvoiceInfoSearchInfo | ||||
| import com.ffii.tsms.modules.project.entity.projections.InvoiceSearchInfo | import com.ffii.tsms.modules.project.entity.projections.InvoiceSearchInfo | ||||
| import com.ffii.tsms.modules.project.entity.projections.ProjectSearchInfo | import com.ffii.tsms.modules.project.entity.projections.ProjectSearchInfo | ||||
| import java.io.Serializable | |||||
| interface ProjectRepository : AbstractRepository<Project, Long> { | interface ProjectRepository : AbstractRepository<Project, Long> { | ||||
| fun findProjectSearchInfoByOrderByCreatedDesc(): List<ProjectSearchInfo> | fun findProjectSearchInfoByOrderByCreatedDesc(): List<ProjectSearchInfo> | ||||
| @@ -13,4 +14,6 @@ interface ProjectRepository : AbstractRepository<Project, Long> { | |||||
| fun findInvoiceSearchInfoById(id: Long): List<InvoiceSearchInfo> | fun findInvoiceSearchInfoById(id: Long): List<InvoiceSearchInfo> | ||||
| fun findInvoiceInfoSearchInfoById(id: Long): List<InvoiceInfoSearchInfo> | fun findInvoiceInfoSearchInfoById(id: Long): List<InvoiceInfoSearchInfo> | ||||
| fun findFirstByIsClpProjectAndIdNotOrderByIdDesc(isClpProject: Boolean, id: Serializable?): Project? | |||||
| } | } | ||||
| @@ -9,6 +9,7 @@ interface ProjectSearchInfo { | |||||
| val id: Long? | val id: Long? | ||||
| val name: String? | val name: String? | ||||
| val code: String? | val code: String? | ||||
| val status: String? | |||||
| @get:Value("#{target.projectCategory.name}") | @get:Value("#{target.projectCategory.name}") | ||||
| val category: String? | val category: String? | ||||
| @@ -96,7 +96,7 @@ open class InvoiceService( | |||||
| open fun getMilestonePaymentWithProjectCode(code: List<String>): List<Map<String, Any>> { | open fun getMilestonePaymentWithProjectCode(code: List<String>): List<Map<String, Any>> { | ||||
| val sql = StringBuilder("select " | val sql = StringBuilder("select " | ||||
| + " p.code, mp.* " | |||||
| + " p.code, mp.*, m.taskGroupId " | |||||
| + " from milestone_payment mp " | + " from milestone_payment mp " | ||||
| + " left join milestone m on mp.milestoneId = m.id " | + " left join milestone m on mp.milestoneId = m.id " | ||||
| + " left join project p on p.id = m.projectId " | + " left join project p on p.id = m.projectId " | ||||
| @@ -108,6 +108,20 @@ open class InvoiceService( | |||||
| return jdbcDao.queryForList(sql.toString(), args) | return jdbcDao.queryForList(sql.toString(), args) | ||||
| } | } | ||||
| open fun getMilestonePaymentId(code: String, paymentMilestone: String): Long{ | |||||
| val sql = StringBuilder(" select" | |||||
| + " mp.id as milestonePaymentId" | |||||
| + " from milestone_payment mp" | |||||
| + " left join milestone m on mp.milestoneId = m.id" | |||||
| + " left join project p on p.id = m.projectId" | |||||
| + " where p.deleted = false" | |||||
| + " and p.code = :code" | |||||
| + " and mp.description = :description " | |||||
| ) | |||||
| val args = mapOf("code" to code, "description" to paymentMilestone) | |||||
| return jdbcDao.queryForInt(sql.toString(), args).toLong() | |||||
| } | |||||
| open fun getInvoiceByInvoiceNo(invoiceNo: String): Invoice { | open fun getInvoiceByInvoiceNo(invoiceNo: String): Invoice { | ||||
| return invoiceRepository.findByInvoiceNo(invoiceNo) | return invoiceRepository.findByInvoiceNo(invoiceNo) | ||||
| } | } | ||||
| @@ -227,17 +241,18 @@ open class InvoiceService( | |||||
| /** | /** | ||||
| * @return true when cellValue Object exist in DB | * @return true when cellValue Object exist in DB | ||||
| */ | */ | ||||
| fun checkStringExists(list: List<Map<String, Any>>, cellValue: Map<String, Any>): Boolean { | |||||
| private fun checkStringExists(list: List<Map<String, Any>>, cellValue: Map<String, Any>): Boolean { | |||||
| // println("LIST-------------: $list") | // println("LIST-------------: $list") | ||||
| // println("CELL VALUE-------------: $cellValue") | // println("CELL VALUE-------------: $cellValue") | ||||
| // println(list.contains(cellValue)) | |||||
| // println(list.any { it["code"] == cellValue["code"] && it["description"] == cellValue["description"] }) | |||||
| // return list.contains(cellValue) | |||||
| return list.any { it["code"] == cellValue["code"] && it["description"] == cellValue["description"] } | |||||
| // println(list.any { it["code"] == cellValue["code"] && it["description"] == cellValue["description"] && it["groupTaskId"] == cellValue["stage"]}) | |||||
| return list.any { it["code"] == cellValue["code"] && it["description"] == cellValue["description"] && it["groupTaskId"] == cellValue["stage"]} | |||||
| } | } | ||||
| open fun checkMilestonePayment( | |||||
| open fun checkMilestonePaymentByStageAndDescription( | |||||
| sheet: Sheet, | sheet: Sheet, | ||||
| startingRow: Int, | startingRow: Int, | ||||
| stageColumnIndex: Int, | |||||
| columnIndex: Int, | columnIndex: Int, | ||||
| invoiceColumnIndex: Int, | invoiceColumnIndex: Int, | ||||
| projectCodeColumnIndex: Int, | projectCodeColumnIndex: Int, | ||||
| @@ -250,15 +265,18 @@ open class InvoiceService( | |||||
| val milestonePaymentCell = row?.getCell(columnIndex) | val milestonePaymentCell = row?.getCell(columnIndex) | ||||
| val invoiceNoCell = row?.getCell(invoiceColumnIndex) | val invoiceNoCell = row?.getCell(invoiceColumnIndex) | ||||
| val projectCodeCell = row?.getCell(projectCodeColumnIndex) | val projectCodeCell = row?.getCell(projectCodeColumnIndex) | ||||
| val stageCell = row?.getCell(stageColumnIndex) | |||||
| if (milestonePaymentCell != null && milestonePaymentCell.cellType == CellType.STRING && | if (milestonePaymentCell != null && milestonePaymentCell.cellType == CellType.STRING && | ||||
| invoiceNoCell != null && invoiceNoCell.cellType == CellType.STRING && | invoiceNoCell != null && invoiceNoCell.cellType == CellType.STRING && | ||||
| projectCodeCell != null && projectCodeCell.cellType == CellType.STRING) | |||||
| projectCodeCell != null && projectCodeCell.cellType == CellType.STRING && | |||||
| stageCell != null && stageCell.cellType == CellType.NUMERIC) | |||||
| { | { | ||||
| val milestonePaymentCellValue = milestonePaymentCell.stringCellValue | val milestonePaymentCellValue = milestonePaymentCell.stringCellValue | ||||
| val invoiceNoCellValue = invoiceNoCell.stringCellValue | val invoiceNoCellValue = invoiceNoCell.stringCellValue | ||||
| val projectCodeCellValue = projectCodeCell.stringCellValue | val projectCodeCellValue = projectCodeCell.stringCellValue | ||||
| val stageCellValue = stageCell.numericCellValue.toInt() | |||||
| val cellValue = mapOf("code" to projectCodeCellValue, "description" to milestonePaymentCellValue) | |||||
| val cellValue = mapOf("code" to projectCodeCellValue, "description" to milestonePaymentCellValue, "groupTaskId" to stageCellValue) | |||||
| if(!checkStringExists(paymentMilestoneWithCode, cellValue)) { | if(!checkStringExists(paymentMilestoneWithCode, cellValue)) { | ||||
| if(!nonExistMilestone.contains(mapOf("paymentMilestone" to milestonePaymentCellValue, "invoiceNo" to invoiceNoCellValue))){ | if(!nonExistMilestone.contains(mapOf("paymentMilestone" to milestonePaymentCellValue, "invoiceNo" to invoiceNoCellValue))){ | ||||
| @@ -359,7 +377,7 @@ open class InvoiceService( | |||||
| val milestonepaymentWithCode = getMilestonePaymentWithProjectCode(projectsCodes) | val milestonepaymentWithCode = getMilestonePaymentWithProjectCode(projectsCodes) | ||||
| // println("newProjectCodes == 0") | // println("newProjectCodes == 0") | ||||
| // println(checkMilestonePayment(sheet, 2, 5, 0, 1, milestonepaymentWithCode)) | // println(checkMilestonePayment(sheet, 2, 5, 0, 1, milestonepaymentWithCode)) | ||||
| val paymenMilestones = checkMilestonePayment(sheet, 2, 5, 0, 1, milestonepaymentWithCode) | |||||
| val paymenMilestones = checkMilestonePaymentByStageAndDescription(sheet, 2, 4,5, 0, 1, milestonepaymentWithCode) | |||||
| if (paymenMilestones.isNotEmpty()){ | if (paymenMilestones.isNotEmpty()){ | ||||
| return InvoiceResponse(false, "Imported Invoice's format is incorrect", newProjectCodes, emptyRowList, invoicesResult, duplicateItemsInInvoice, paymenMilestones) | return InvoiceResponse(false, "Imported Invoice's format is incorrect", newProjectCodes, emptyRowList, invoicesResult, duplicateItemsInInvoice, paymenMilestones) | ||||
| } | } | ||||
| @@ -376,6 +394,9 @@ open class InvoiceService( | |||||
| } | } | ||||
| for (i in 2..sheet.lastRowNum){ | for (i in 2..sheet.lastRowNum){ | ||||
| val paymentMilestoneId = getMilestonePaymentId(ExcelUtils.getCell(sheet, i, 1).stringCellValue, ExcelUtils.getCell(sheet, i, 5).stringCellValue) | |||||
| println("paymentMilestoneId--------------: $paymentMilestoneId") | |||||
| val milestonePayment = milestonePaymentRepository.findById(paymentMilestoneId).orElseThrow() | |||||
| val invoice = Invoice().apply { | val invoice = Invoice().apply { | ||||
| invoiceNo = ExcelUtils.getCell(sheet, i, 0).stringCellValue | invoiceNo = ExcelUtils.getCell(sheet, i, 0).stringCellValue | ||||
| projectCode = ExcelUtils.getCell(sheet, i, 1).stringCellValue | projectCode = ExcelUtils.getCell(sheet, i, 1).stringCellValue | ||||
| @@ -390,6 +411,7 @@ open class InvoiceService( | |||||
| invoiceDate = ExcelUtils.getCell(sheet, i, 10).dateCellValue.toInstant().atZone(ZoneId.systemDefault()).toLocalDate() | invoiceDate = ExcelUtils.getCell(sheet, i, 10).dateCellValue.toInstant().atZone(ZoneId.systemDefault()).toLocalDate() | ||||
| dueDate = ExcelUtils.getCell(sheet, i, 11).dateCellValue.toInstant().atZone(ZoneId.systemDefault()).toLocalDate() | dueDate = ExcelUtils.getCell(sheet, i, 11).dateCellValue.toInstant().atZone(ZoneId.systemDefault()).toLocalDate() | ||||
| issueAmount = ExcelUtils.getCell(sheet, i, 12).numericCellValue.toBigDecimal() | issueAmount = ExcelUtils.getCell(sheet, i, 12).numericCellValue.toBigDecimal() | ||||
| this.milestonePayment = milestonePayment | |||||
| } | } | ||||
| saveAndFlush(invoice) | saveAndFlush(invoice) | ||||
| } | } | ||||
| @@ -40,8 +40,11 @@ open class ProjectsService( | |||||
| private val milestoneRepository: MilestoneRepository, | private val milestoneRepository: MilestoneRepository, | ||||
| private val gradeAllocationRepository: GradeAllocationRepository, | private val gradeAllocationRepository: GradeAllocationRepository, | ||||
| private val projectTaskRepository: ProjectTaskRepository, | private val projectTaskRepository: ProjectTaskRepository, | ||||
| private val milestonePaymentRepository: MilestonePaymentRepository, private val taskGroupRepository: TaskGroupRepository, | |||||
| private val timesheetRepository: TimesheetRepository | |||||
| private val milestonePaymentRepository: MilestonePaymentRepository, | |||||
| private val taskGroupRepository: TaskGroupRepository, | |||||
| private val timesheetRepository: TimesheetRepository, | |||||
| private val taskTemplateRepository: TaskTemplateRepository, | |||||
| private val subsidiaryRepository: SubsidiaryRepository, private val subsidiaryContactRepository: SubsidiaryContactRepository | |||||
| ) { | ) { | ||||
| open fun allProjects(): List<ProjectSearchInfo> { | open fun allProjects(): List<ProjectSearchInfo> { | ||||
| return projectRepository.findProjectSearchInfoByOrderByCreatedDesc() | return projectRepository.findProjectSearchInfoByOrderByCreatedDesc() | ||||
| @@ -60,34 +63,42 @@ open class ProjectsService( | |||||
| } | } | ||||
| open fun markDeleted(id: Long) { | open fun markDeleted(id: Long) { | ||||
| projectRepository.save(projectRepository.findById(id).orElseThrow().apply { deleted = true }) | |||||
| projectRepository.save(projectRepository.findById(id).orElseThrow() | |||||
| .apply { | |||||
| deleted = true | |||||
| status = "Deleted" | |||||
| }) | |||||
| } | } | ||||
| open fun allAssignedProjects(): List<AssignedProject> { | open fun allAssignedProjects(): List<AssignedProject> { | ||||
| return SecurityUtils.getUser().getOrNull()?.let { user -> | return SecurityUtils.getUser().getOrNull()?.let { user -> | ||||
| staffRepository.findByUserId(user.id).getOrNull()?.let { staff -> | staffRepository.findByUserId(user.id).getOrNull()?.let { staff -> | ||||
| staffAllocationRepository.findAssignedProjectsByStaff(staff) | staffAllocationRepository.findAssignedProjectsByStaff(staff) | ||||
| .mapNotNull { it.project?.let { project -> | |||||
| val timesheetHours = timesheetRepository.totalHoursConsumedByProject(project) | |||||
| AssignedProject( | |||||
| id = project.id!!, | |||||
| code = project.code!!, | |||||
| name = project.name!!, | |||||
| tasks = projectTaskRepository.findAllByProject(project).mapNotNull { pt -> pt.task }, | |||||
| milestones = milestoneRepository.findAllByProject(project) | |||||
| .filter { milestone -> milestone.taskGroup?.id != null } | |||||
| .associateBy { milestone -> milestone.taskGroup!!.id!! } | |||||
| .mapValues { (_, milestone) -> MilestoneInfo( | |||||
| startDate = milestone.startDate?.format(DateTimeFormatter.ISO_LOCAL_DATE), | |||||
| endDate = milestone.endDate?.format(DateTimeFormatter.ISO_LOCAL_DATE) | |||||
| ) }, | |||||
| hoursAllocated = project.totalManhour ?: 0.0, | |||||
| hoursAllocatedOther = 0.0, | |||||
| hoursSpent = timesheetHours.normalConsumed, | |||||
| hoursSpentOther = timesheetHours.otConsumed | |||||
| ) | |||||
| } } | |||||
| .mapNotNull { | |||||
| it.project?.let { project -> | |||||
| val timesheetHours = timesheetRepository.totalHoursConsumedByProject(project) | |||||
| AssignedProject( | |||||
| id = project.id!!, | |||||
| code = project.code!!, | |||||
| name = project.name!!, | |||||
| tasks = projectTaskRepository.findAllByProject(project).mapNotNull { pt -> pt.task }, | |||||
| milestones = milestoneRepository.findAllByProject(project) | |||||
| .filter { milestone -> milestone.taskGroup?.id != null } | |||||
| .associateBy { milestone -> milestone.taskGroup!!.id!! } | |||||
| .mapValues { (_, milestone) -> | |||||
| MilestoneInfo( | |||||
| startDate = milestone.startDate?.format(DateTimeFormatter.ISO_LOCAL_DATE), | |||||
| endDate = milestone.endDate?.format(DateTimeFormatter.ISO_LOCAL_DATE) | |||||
| ) | |||||
| }, | |||||
| hoursAllocated = project.totalManhour ?: 0.0, | |||||
| hoursAllocatedOther = 0.0, | |||||
| hoursSpent = timesheetHours.normalConsumed, | |||||
| hoursSpentOther = timesheetHours.otConsumed | |||||
| ) | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } ?: emptyList() | } ?: emptyList() | ||||
| } | } | ||||
| @@ -102,10 +113,12 @@ open class ProjectsService( | |||||
| milestones = milestoneRepository.findAllByProject(project) | milestones = milestoneRepository.findAllByProject(project) | ||||
| .filter { milestone -> milestone.taskGroup?.id != null } | .filter { milestone -> milestone.taskGroup?.id != null } | ||||
| .associateBy { milestone -> milestone.taskGroup!!.id!! } | .associateBy { milestone -> milestone.taskGroup!!.id!! } | ||||
| .mapValues { (_, milestone) -> MilestoneInfo( | |||||
| startDate = milestone.startDate?.format(DateTimeFormatter.ISO_LOCAL_DATE), | |||||
| endDate = milestone.endDate?.format(DateTimeFormatter.ISO_LOCAL_DATE) | |||||
| ) } | |||||
| .mapValues { (_, milestone) -> | |||||
| MilestoneInfo( | |||||
| startDate = milestone.startDate?.format(DateTimeFormatter.ISO_LOCAL_DATE), | |||||
| endDate = milestone.endDate?.format(DateTimeFormatter.ISO_LOCAL_DATE) | |||||
| ) | |||||
| } | |||||
| ) | ) | ||||
| } | } | ||||
| } | } | ||||
| @@ -114,6 +127,19 @@ open class ProjectsService( | |||||
| return projectCategoryRepository.findAll() | return projectCategoryRepository.findAll() | ||||
| } | } | ||||
| open fun createProjectCode(isClpProject: Boolean?, project: Project): String { | |||||
| val checkIsClpProject = isClpProject != null && isClpProject | |||||
| val prefix = if (checkIsClpProject) "C" else "M" | |||||
| val latestProjectCode = projectRepository.findFirstByIsClpProjectAndIdNotOrderByIdDesc(checkIsClpProject, project.id) | |||||
| if (latestProjectCode != null) { | |||||
| val lastFix = latestProjectCode.code!!.split("-")[1] //C-0001 -> 0001 | |||||
| return "$prefix-" + String.format("%04d", lastFix.toLong() + 1L) | |||||
| } else { | |||||
| return "$prefix-0001" | |||||
| } | |||||
| } | |||||
| @Transactional | @Transactional | ||||
| open fun saveProject(request: NewProjectRequest): NewProjectResponse { | open fun saveProject(request: NewProjectRequest): NewProjectResponse { | ||||
| val projectCategory = | val projectCategory = | ||||
| @@ -124,9 +150,12 @@ open class ProjectsService( | |||||
| val location = locationRepository.findById(request.locationId).orElseThrow() | val location = locationRepository.findById(request.locationId).orElseThrow() | ||||
| val buildingTypes = buildingTypeRepository.findAllById(request.buildingTypeIds).toMutableSet() | val buildingTypes = buildingTypeRepository.findAllById(request.buildingTypeIds).toMutableSet() | ||||
| val workNatures = workNatureRepository.findAllById(request.workNatureIds).toMutableSet() | val workNatures = workNatureRepository.findAllById(request.workNatureIds).toMutableSet() | ||||
| val taskTemplate = | |||||
| if (request.taskTemplateId != null && request.taskTemplateId > 0) taskTemplateRepository.findById(request.taskTemplateId) | |||||
| .orElseThrow() else null | |||||
| val teamLead = staffRepository.findById(request.projectLeadId).orElseThrow() | val teamLead = staffRepository.findById(request.projectLeadId).orElseThrow() | ||||
| val customer = customerService.findCustomer(request.clientId) | val customer = customerService.findCustomer(request.clientId) | ||||
| val subsidiaryContact = subsidiaryContactRepository.findById(request.clientContactId).orElse(null) | |||||
| val clientContact = customerContactService.findByContactId(request.clientContactId) | val clientContact = customerContactService.findByContactId(request.clientContactId) | ||||
| val customerSubsidiary = request.clientSubsidiaryId?.let { subsidiaryService.findSubsidiary(it) } | val customerSubsidiary = request.clientSubsidiaryId?.let { subsidiaryService.findSubsidiary(it) } | ||||
| @@ -134,15 +163,24 @@ open class ProjectsService( | |||||
| val taskGroupMap = tasksService.allTaskGroups().associateBy { it.id } | val taskGroupMap = tasksService.allTaskGroups().associateBy { it.id } | ||||
| val gradeMap = gradeService.allGrades().associateBy { it.id } | val gradeMap = gradeService.allGrades().associateBy { it.id } | ||||
| val project = if (request.projectId != null && request.projectId > 0) projectRepository.findById(request.projectId).orElseThrow() else Project() | |||||
| val project = | |||||
| if (request.projectId != null && request.projectId > 0) projectRepository.findById(request.projectId) | |||||
| .orElseThrow() else Project() | |||||
| project.apply { | project.apply { | ||||
| name = request.projectName | name = request.projectName | ||||
| description = request.projectDescription | description = request.projectDescription | ||||
| code = request.projectCode | |||||
| println(this.code) | |||||
| println(this.code.isNullOrEmpty()) | |||||
| code = if (this.code.isNullOrEmpty()) createProjectCode(request.isClpProject, project) else this.code | |||||
| expectedTotalFee = request.expectedProjectFee | expectedTotalFee = request.expectedProjectFee | ||||
| totalManhour = request.totalManhour | totalManhour = request.totalManhour | ||||
| actualStart = request.projectActualStart | actualStart = request.projectActualStart | ||||
| actualEnd = request.projectActualEnd | actualEnd = request.projectActualEnd | ||||
| status = if (this.status == "Deleted" || this.deleted == true) "Deleted" | |||||
| else if (this.actualStart != null && this.actualEnd != null) "Completed" | |||||
| else if (this.actualStart != null) "On-going" | |||||
| else "Pending To Start" | |||||
| isClpProject = request.isClpProject | |||||
| this.projectCategory = projectCategory | this.projectCategory = projectCategory | ||||
| this.fundingType = fundingType | this.fundingType = fundingType | ||||
| @@ -151,12 +189,13 @@ open class ProjectsService( | |||||
| this.location = location | this.location = location | ||||
| this.buildingTypes = buildingTypes | this.buildingTypes = buildingTypes | ||||
| this.workNatures = workNatures | this.workNatures = workNatures | ||||
| this.taskTemplate = taskTemplate | |||||
| this.teamLead = teamLead | this.teamLead = teamLead | ||||
| this.customer = customer | this.customer = customer | ||||
| custLeadName = clientContact.name | |||||
| custLeadEmail = clientContact.email | |||||
| custLeadPhone = clientContact.phone | |||||
| custLeadName = if (request.isSubsidiaryContact == null || !request.isSubsidiaryContact) clientContact.name else subsidiaryContact.name | |||||
| custLeadEmail = if (request.isSubsidiaryContact == null || !request.isSubsidiaryContact) clientContact.email else subsidiaryContact.email | |||||
| custLeadPhone = if (request.isSubsidiaryContact == null || !request.isSubsidiaryContact) clientContact.phone else subsidiaryContact.phone | |||||
| this.customerSubsidiary = customerSubsidiary | this.customerSubsidiary = customerSubsidiary | ||||
| } | } | ||||
| @@ -165,7 +204,10 @@ open class ProjectsService( | |||||
| val tasksToSave = mutableListOf<ProjectTask>() | val tasksToSave = mutableListOf<ProjectTask>() | ||||
| val milestones = request.taskGroups.entries.map { (taskStageId, taskGroupAllocation) -> | val milestones = request.taskGroups.entries.map { (taskStageId, taskGroupAllocation) -> | ||||
| val taskGroup = taskGroupRepository.findById(taskStageId).orElse(TaskGroup()) ?: TaskGroup() | val taskGroup = taskGroupRepository.findById(taskStageId).orElse(TaskGroup()) ?: TaskGroup() | ||||
| val milestone = if (project.id != null && project.id!! > 0L) milestoneRepository.findByProjectAndTaskGroup(project, taskGroup) ?: Milestone() else Milestone() | |||||
| val milestone = if (project.id != null && project.id!! > 0L) milestoneRepository.findByProjectAndTaskGroup( | |||||
| project, | |||||
| taskGroup | |||||
| ) ?: Milestone() else Milestone() | |||||
| milestone.apply { | milestone.apply { | ||||
| val newMilestone = this | val newMilestone = this | ||||
| val requestMilestone = request.milestones[taskStageId] | val requestMilestone = request.milestones[taskStageId] | ||||
| @@ -177,7 +219,8 @@ open class ProjectsService( | |||||
| this.milestonePayments.removeAll(this.milestonePayments) | this.milestonePayments.removeAll(this.milestonePayments) | ||||
| } | } | ||||
| requestMilestone?.payments?.map { | requestMilestone?.payments?.map { | ||||
| val milestonePayment = milestonePaymentRepository.findById(it.id).orElse(MilestonePayment()) ?:MilestonePayment() | |||||
| val milestonePayment = | |||||
| milestonePaymentRepository.findById(it.id).orElse(MilestonePayment()) ?: MilestonePayment() | |||||
| this.milestonePayments.add(milestonePayment.apply { | this.milestonePayments.add(milestonePayment.apply { | ||||
| this.milestone = newMilestone | this.milestone = newMilestone | ||||
| this.description = it.description | this.description = it.description | ||||
| @@ -190,7 +233,11 @@ open class ProjectsService( | |||||
| this.stagePercentAllocation = taskGroupAllocation.percentAllocation | this.stagePercentAllocation = taskGroupAllocation.percentAllocation | ||||
| taskGroupAllocation.taskIds.map { taskId -> | taskGroupAllocation.taskIds.map { taskId -> | ||||
| val projectTask = if (project.id != null && project.id!! > 0L) projectTaskRepository.findByProjectAndTask(project, allTasksMap[taskId]!!) ?:ProjectTask() else ProjectTask() | |||||
| val projectTask = | |||||
| if (project.id != null && project.id!! > 0L) projectTaskRepository.findByProjectAndTask( | |||||
| project, | |||||
| allTasksMap[taskId]!! | |||||
| ) ?: ProjectTask() else ProjectTask() | |||||
| projectTask.apply { | projectTask.apply { | ||||
| this.project = project | this.project = project | ||||
| @@ -205,7 +252,11 @@ open class ProjectsService( | |||||
| // Grade allocation (from manhourPercentageByGrade) | // Grade allocation (from manhourPercentageByGrade) | ||||
| val gradeAllocations = request.manhourPercentageByGrade.entries.map { | val gradeAllocations = request.manhourPercentageByGrade.entries.map { | ||||
| val gradeAllocation = if (project.id != null && project.id!! > 0L) gradeAllocationRepository.findByProjectAndGrade(project, gradeMap[it.key]!!) ?: GradeAllocation() else GradeAllocation() | |||||
| val gradeAllocation = | |||||
| if (project.id != null && project.id!! > 0L) gradeAllocationRepository.findByProjectAndGrade( | |||||
| project, | |||||
| gradeMap[it.key]!! | |||||
| ) ?: GradeAllocation() else GradeAllocation() | |||||
| gradeAllocation.apply { | gradeAllocation.apply { | ||||
| this.project = project | this.project = project | ||||
| @@ -225,7 +276,8 @@ open class ProjectsService( | |||||
| val milestonesToDelete = milestoneRepository.findAllByProject(project).subtract(milestones.toSet()) | val milestonesToDelete = milestoneRepository.findAllByProject(project).subtract(milestones.toSet()) | ||||
| val tasksToDelete = projectTaskRepository.findAllByProject(project).subtract(tasksToSave.toSet()) | val tasksToDelete = projectTaskRepository.findAllByProject(project).subtract(tasksToSave.toSet()) | ||||
| val gradeAllocationsToDelete = gradeAllocationRepository.findByProject(project).subtract(gradeAllocations.toSet()) | |||||
| val gradeAllocationsToDelete = | |||||
| gradeAllocationRepository.findByProject(project).subtract(gradeAllocations.toSet()) | |||||
| milestoneRepository.deleteAll(milestonesToDelete) | milestoneRepository.deleteAll(milestonesToDelete) | ||||
| projectTaskRepository.deleteAll(tasksToDelete) | projectTaskRepository.deleteAll(tasksToDelete) | ||||
| gradeAllocationRepository.deleteAll(gradeAllocationsToDelete) | gradeAllocationRepository.deleteAll(gradeAllocationsToDelete) | ||||
| @@ -236,11 +288,14 @@ open class ProjectsService( | |||||
| // Staff allocation | // Staff allocation | ||||
| val allocatedStaff = staffRepository.findAllById(request.allocatedStaffIds) | val allocatedStaff = staffRepository.findAllById(request.allocatedStaffIds) | ||||
| val staffAllocations = allocatedStaff.map { staff -> StaffAllocation().apply { | |||||
| this.project = savedProject | |||||
| this.staff = staff | |||||
| } } | |||||
| val staffAllocationsToDelete = staffAllocationRepository.findByProject(savedProject).subtract(staffAllocations.toSet()) | |||||
| val staffAllocations = allocatedStaff.map { staff -> | |||||
| StaffAllocation().apply { | |||||
| this.project = savedProject | |||||
| this.staff = staff | |||||
| } | |||||
| } | |||||
| val staffAllocationsToDelete = | |||||
| staffAllocationRepository.findByProject(savedProject).subtract(staffAllocations.toSet()) | |||||
| staffAllocationRepository.deleteAll(staffAllocationsToDelete) | staffAllocationRepository.deleteAll(staffAllocationsToDelete) | ||||
| staffAllocationRepository.saveAll(staffAllocations) | staffAllocationRepository.saveAll(staffAllocations) | ||||
| @@ -260,7 +315,9 @@ open class ProjectsService( | |||||
| val project = projectRepository.findById(projectId) | val project = projectRepository.findById(projectId) | ||||
| return project.getOrNull()?.let { | return project.getOrNull()?.let { | ||||
| val customerContact = it.customer?.id?.let { customerId -> customerContactService.findAllByCustomerId(customerId) } ?: emptyList() | |||||
| val customerContact = | |||||
| it.customer?.id?.let { customerId -> customerContactService.findAllByCustomerId(customerId) } | |||||
| ?: emptyList() | |||||
| val milestoneMap = it.milestones | val milestoneMap = it.milestones | ||||
| .filter { milestone -> milestone.taskGroup?.id != null } | .filter { milestone -> milestone.taskGroup?.id != null } | ||||
| @@ -276,11 +333,14 @@ open class ProjectsService( | |||||
| projectLeadId = it.teamLead?.id, | projectLeadId = it.teamLead?.id, | ||||
| projectActualStart = it.actualStart, | projectActualStart = it.actualStart, | ||||
| projectActualEnd = it.actualEnd, | projectActualEnd = it.actualEnd, | ||||
| projectStatus = it.status, | |||||
| isClpProject = it.isClpProject, | |||||
| serviceTypeId = it.serviceType?.id, | serviceTypeId = it.serviceType?.id, | ||||
| fundingTypeId = it.fundingType?.id, | fundingTypeId = it.fundingType?.id, | ||||
| contractTypeId = it.contractType?.id, | contractTypeId = it.contractType?.id, | ||||
| locationId = it.location?.id, | locationId = it.location?.id, | ||||
| buildingTypeIds = it.buildingTypes.mapNotNull { buildingType -> buildingType.id }, | |||||
| taskTemplateId = it.taskTemplate?.id, | |||||
| buildingTypeIds = it.buildingTypes.mapNotNull { buildingType -> buildingType.id }, | |||||
| workNatureIds = it.workNatures.mapNotNull { workNature -> workNature.id }, | workNatureIds = it.workNatures.mapNotNull { workNature -> workNature.id }, | ||||
| clientId = it.customer?.id, | clientId = it.customer?.id, | ||||
| clientContactId = customerContact.find { contact -> contact.name == it.custLeadName }?.id, | clientContactId = customerContact.find { contact -> contact.name == it.custLeadName }?.id, | ||||
| @@ -292,21 +352,28 @@ open class ProjectsService( | |||||
| taskGroups = projectTaskRepository.findAllByProject(it) | taskGroups = projectTaskRepository.findAllByProject(it) | ||||
| .mapNotNull { projectTask -> if (projectTask.task?.taskGroup?.id != null) projectTask.task else null } | .mapNotNull { projectTask -> if (projectTask.task?.taskGroup?.id != null) projectTask.task else null } | ||||
| .groupBy { task -> task.taskGroup!!.id!! } | .groupBy { task -> task.taskGroup!!.id!! } | ||||
| .mapValues { (taskGroupId, tasks) -> TaskGroupAllocation( | |||||
| taskIds = tasks.mapNotNull { task -> task.id }, | |||||
| percentAllocation = milestoneMap[taskGroupId]?.stagePercentAllocation ?: 0.0 | |||||
| ) }, | |||||
| allocatedStaffIds = staffAllocationRepository.findByProject(it).mapNotNull { allocation -> allocation.staff?.id }, | |||||
| milestones = milestoneMap.mapValues { (_, milestone) -> com.ffii.tsms.modules.project.web.models.Milestone( | |||||
| startDate = milestone.startDate?.format(DateTimeFormatter.ISO_LOCAL_DATE), | |||||
| endDate = milestone.endDate?.format(DateTimeFormatter.ISO_LOCAL_DATE), | |||||
| payments = milestone.milestonePayments.map { payment -> PaymentInputs( | |||||
| id = payment.id!!, | |||||
| amount = payment.amount!!, | |||||
| description = payment.description!!, | |||||
| date = payment.date!!.format(DateTimeFormatter.ISO_LOCAL_DATE) | |||||
| )} | |||||
| )}, | |||||
| .mapValues { (taskGroupId, tasks) -> | |||||
| TaskGroupAllocation( | |||||
| taskIds = tasks.mapNotNull { task -> task.id }, | |||||
| percentAllocation = milestoneMap[taskGroupId]?.stagePercentAllocation ?: 0.0 | |||||
| ) | |||||
| }, | |||||
| allocatedStaffIds = staffAllocationRepository.findByProject(it) | |||||
| .mapNotNull { allocation -> allocation.staff?.id }, | |||||
| milestones = milestoneMap.mapValues { (_, milestone) -> | |||||
| com.ffii.tsms.modules.project.web.models.Milestone( | |||||
| startDate = milestone.startDate?.format(DateTimeFormatter.ISO_LOCAL_DATE), | |||||
| endDate = milestone.endDate?.format(DateTimeFormatter.ISO_LOCAL_DATE), | |||||
| payments = milestone.milestonePayments.map { payment -> | |||||
| PaymentInputs( | |||||
| id = payment.id!!, | |||||
| amount = payment.amount!!, | |||||
| description = payment.description!!, | |||||
| date = payment.date!!.format(DateTimeFormatter.ISO_LOCAL_DATE) | |||||
| ) | |||||
| } | |||||
| ) | |||||
| }, | |||||
| expectedProjectFee = it.expectedTotalFee | expectedProjectFee = it.expectedTotalFee | ||||
| ) | ) | ||||
| } | } | ||||
| @@ -13,6 +13,8 @@ data class EditProjectDetails( | |||||
| val projectLeadId: Long?, | val projectLeadId: Long?, | ||||
| val projectActualStart: LocalDate?, | val projectActualStart: LocalDate?, | ||||
| val projectActualEnd: LocalDate?, | val projectActualEnd: LocalDate?, | ||||
| val projectStatus: String?, | |||||
| val isClpProject: Boolean?, | |||||
| val serviceTypeId: Long?, | val serviceTypeId: Long?, | ||||
| val fundingTypeId: Long?, | val fundingTypeId: Long?, | ||||
| @@ -20,6 +22,7 @@ data class EditProjectDetails( | |||||
| val locationId: Long?, | val locationId: Long?, | ||||
| val buildingTypeIds: List<Long>, | val buildingTypeIds: List<Long>, | ||||
| val workNatureIds: List<Long>, | val workNatureIds: List<Long>, | ||||
| val taskTemplateId: Long?, | |||||
| // Client details | // Client details | ||||
| val clientId: Long?, | val clientId: Long?, | ||||
| @@ -1,12 +1,13 @@ | |||||
| package com.ffii.tsms.modules.project.web.models | package com.ffii.tsms.modules.project.web.models | ||||
| import com.ffii.tsms.modules.data.entity.SubsidiaryContact | |||||
| import jakarta.validation.constraints.NotBlank | import jakarta.validation.constraints.NotBlank | ||||
| import java.time.LocalDate | import java.time.LocalDate | ||||
| data class NewProjectRequest( | data class NewProjectRequest( | ||||
| // Project details | // Project details | ||||
| @field:NotBlank(message = "project code cannot be empty") | |||||
| val projectCode: String, | |||||
| // @field:NotBlank(message = "project code cannot be empty") | |||||
| // val projectCode: String, | |||||
| @field:NotBlank(message = "project name cannot be empty") | @field:NotBlank(message = "project name cannot be empty") | ||||
| val projectName: String, | val projectName: String, | ||||
| val projectCategoryId: Long, | val projectCategoryId: Long, | ||||
| @@ -15,6 +16,7 @@ data class NewProjectRequest( | |||||
| val projectId: Long?, | val projectId: Long?, | ||||
| val projectActualStart: LocalDate?, | val projectActualStart: LocalDate?, | ||||
| val projectActualEnd: LocalDate?, | val projectActualEnd: LocalDate?, | ||||
| val isClpProject: Boolean?, | |||||
| val serviceTypeId: Long, | val serviceTypeId: Long, | ||||
| val fundingTypeId: Long, | val fundingTypeId: Long, | ||||
| @@ -22,6 +24,8 @@ data class NewProjectRequest( | |||||
| val locationId: Long, | val locationId: Long, | ||||
| val buildingTypeIds: List<Long>, | val buildingTypeIds: List<Long>, | ||||
| val workNatureIds: List<Long>, | val workNatureIds: List<Long>, | ||||
| val taskTemplateId: Long?, | |||||
| val isSubsidiaryContact: Boolean?, | |||||
| // Client details | // Client details | ||||
| val clientId: Long, | val clientId: Long, | ||||
| @@ -0,0 +1,13 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset cyril:project | |||||
| ALTER TABLE `project` | |||||
| ADD COLUMN `taskTemplateId` INT NULL DEFAULT NULL AFTER `customerSubsidiaryId`, | |||||
| ADD INDEX `FK_PROJECT_ON_TASKTEMPLATEID` (`taskTemplateId` ASC) VISIBLE; | |||||
| ; | |||||
| ALTER TABLE `project` | |||||
| ADD CONSTRAINT `FK_PROJECT_ON_TASKTEMPLATEID` | |||||
| FOREIGN KEY (`taskTemplateId`) | |||||
| REFERENCES `task_template` (`id`) | |||||
| ON DELETE NO ACTION | |||||
| ON UPDATE NO ACTION; | |||||
| @@ -0,0 +1,5 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset cyril:project | |||||
| ALTER TABLE `project` | |||||
| ADD COLUMN `status` VARCHAR(40) NULL DEFAULT 'Pending to Start' AFTER `taskTemplateId`; | |||||
| @@ -0,0 +1,5 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset cyril:project | |||||
| ALTER TABLE `project` | |||||
| ADD COLUMN `isClpProject` TINYINT NULL DEFAULT 0 AFTER `status`; | |||||