@@ -1,97 +0,0 @@ | |||||
package com.ffii.tsms.modules.common.service | |||||
import com.ffii.tsms.modules.project.entity.Project | |||||
import org.apache.poi.ss.usermodel.Cell | |||||
import org.apache.poi.ss.usermodel.CellStyle | |||||
import org.apache.poi.ss.usermodel.HorizontalAlignment | |||||
import org.apache.poi.ss.usermodel.Row | |||||
import org.apache.poi.ss.usermodel.Sheet | |||||
import org.apache.poi.ss.usermodel.Workbook | |||||
import org.apache.poi.ss.util.CellRangeAddress | |||||
import org.apache.poi.ss.util.CellUtil | |||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook | |||||
import org.springframework.core.io.ClassPathResource | |||||
import org.springframework.stereotype.Service | |||||
import java.io.ByteArrayOutputStream | |||||
import java.io.IOException | |||||
import java.time.LocalDate | |||||
import java.time.LocalDateTime | |||||
import java.time.format.DateTimeFormatter | |||||
@Service | |||||
open class ExcelReportService { | |||||
private val DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd") | |||||
private val FORMATTED_TODAY = LocalDate.now().format(DATE_FORMATTER) | |||||
private val EX02_PROJECT_CASH_FLOW_REPORT = "templates/report/EX02_Project Cash Flow Report.xlsx" | |||||
// ==============================|| GENERATE REPORT ||============================== // | |||||
@Throws(IOException::class) | |||||
fun generateEX02ProjectCashFlowReport(project: Project): ByteArray { | |||||
// Generate the Excel report with query results | |||||
val workbook: Workbook = createEX02ProjectCashFlowReport(project, EX02_PROJECT_CASH_FLOW_REPORT) | |||||
// Write the workbook to a ByteArrayOutputStream | |||||
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() | |||||
workbook.write(outputStream) | |||||
workbook.close() | |||||
return outputStream.toByteArray() | |||||
} | |||||
// ==============================|| CREATE REPORT ||============================== // | |||||
@Throws(IOException::class) | |||||
private fun createEX02ProjectCashFlowReport( | |||||
project: Project, | |||||
templatePath: String, | |||||
): Workbook { | |||||
// please create a new function for each report template | |||||
val resource = ClassPathResource(templatePath) | |||||
val templateInputStream = resource.inputStream | |||||
val workbook: Workbook = XSSFWorkbook(templateInputStream) | |||||
val sheet: Sheet = workbook.getSheetAt(0) | |||||
// val alignLeftStyle: CellStyle = workbook.createCellStyle().apply { | |||||
// alignment = HorizontalAlignment.LEFT // Set the alignment to left | |||||
// } | |||||
// | |||||
// val alignRightStyle: CellStyle = workbook.createCellStyle().apply { | |||||
// alignment = HorizontalAlignment.RIGHT // Set the alignment to right | |||||
// } | |||||
var rowIndex = 1 // Assuming the location is in (1,2), which is the report date field | |||||
var columnIndex = 2 | |||||
sheet.getRow(rowIndex).getCell(columnIndex).apply { | |||||
setCellValue(FORMATTED_TODAY) | |||||
} | |||||
rowIndex = 2 | |||||
sheet.getRow(rowIndex).getCell(columnIndex).apply { | |||||
setCellValue(project.code) | |||||
} | |||||
rowIndex = 3 | |||||
sheet.getRow(rowIndex).getCell(columnIndex).apply { | |||||
setCellValue(project.name) | |||||
} | |||||
rowIndex = 4 | |||||
sheet.getRow(rowIndex).getCell(columnIndex).apply { | |||||
setCellValue(if (project.customer?.name == null) "N/A" else project.customer?.name) | |||||
} | |||||
rowIndex = 5 | |||||
sheet.getRow(rowIndex).getCell(columnIndex).apply { | |||||
setCellValue(if (project.teamLead?.team?.name == null) "N/A" else project.teamLead?.team?.name) | |||||
} | |||||
rowIndex = 9 | |||||
sheet.getRow(rowIndex).apply { | |||||
getCell(1).setCellValue(project.expectedTotalFee!! * 0.8) | |||||
getCell(2).setCellValue(project.expectedTotalFee!!) | |||||
} | |||||
return workbook | |||||
} | |||||
} |
@@ -0,0 +1,43 @@ | |||||
package com.ffii.tsms.modules.project.entity | |||||
import com.ffii.core.entity.BaseEntity | |||||
import com.ffii.tsms.modules.data.entity.Customer | |||||
import jakarta.persistence.Column | |||||
import jakarta.persistence.Entity | |||||
import jakarta.persistence.JoinColumn | |||||
import jakarta.persistence.ManyToOne | |||||
import jakarta.persistence.Table | |||||
import jakarta.validation.constraints.NotNull | |||||
import java.time.LocalDate | |||||
@Entity | |||||
@Table(name = "invoice") | |||||
open class Invoice : BaseEntity<Long>(){ | |||||
@NotNull | |||||
@Column(name = "invoiceNo", length = 45) | |||||
open var invoiceNo: String? = null | |||||
@NotNull | |||||
@ManyToOne | |||||
@JoinColumn(name = "milestonePaymentId") | |||||
open var milestonePayment: MilestonePayment? = null | |||||
@NotNull | |||||
@Column(name = "invoiceDate") | |||||
open var invoiceDate: LocalDate? = null | |||||
@NotNull | |||||
@Column(name = "dueDate") | |||||
open var dueDate: LocalDate? = null | |||||
@Column(name = "receiptDate") | |||||
open var receiptDate: LocalDate? = null | |||||
@NotNull | |||||
@Column(name = "unpaidAmount") | |||||
open var unpaidAmount: Double? = null | |||||
@Column(name = "paidAmount") | |||||
open var paidAmount: Double? = null | |||||
} |
@@ -0,0 +1,9 @@ | |||||
package com.ffii.tsms.modules.project.entity | |||||
import com.ffii.core.support.AbstractRepository | |||||
interface InvoiceRepository : AbstractRepository<Invoice, Long> { | |||||
fun findAllByPaidAmountIsNotNullAndMilestonePaymentIn(milestonePayment: List<MilestonePayment>): List<Invoice> | |||||
} |
@@ -1,5 +1,6 @@ | |||||
package com.ffii.tsms.modules.project.entity | package com.ffii.tsms.modules.project.entity | ||||
import com.fasterxml.jackson.annotation.JsonManagedReference | |||||
import com.ffii.core.entity.IdEntity | import com.ffii.core.entity.IdEntity | ||||
import jakarta.persistence.* | import jakarta.persistence.* | ||||
import jakarta.validation.constraints.NotNull | import jakarta.validation.constraints.NotNull | ||||
@@ -15,6 +16,7 @@ open class Milestone : IdEntity<Long>() { | |||||
@Column(name = "description") | @Column(name = "description") | ||||
open var description: String? = null | open var description: String? = null | ||||
@JsonManagedReference | |||||
@OneToMany(mappedBy = "milestone", cascade = [CascadeType.ALL], orphanRemoval = true) | @OneToMany(mappedBy = "milestone", cascade = [CascadeType.ALL], orphanRemoval = true) | ||||
open var milestonePayments: MutableList<MilestonePayment> = mutableListOf() | open var milestonePayments: MutableList<MilestonePayment> = mutableListOf() | ||||
@@ -1,5 +1,6 @@ | |||||
package com.ffii.tsms.modules.project.entity | package com.ffii.tsms.modules.project.entity | ||||
import com.fasterxml.jackson.annotation.JsonBackReference | |||||
import com.ffii.core.entity.IdEntity | import com.ffii.core.entity.IdEntity | ||||
import jakarta.persistence.* | import jakarta.persistence.* | ||||
import jakarta.validation.constraints.NotNull | import jakarta.validation.constraints.NotNull | ||||
@@ -21,6 +22,7 @@ open class MilestonePayment : IdEntity<Long>() { | |||||
open var description: String? = null | open var description: String? = null | ||||
@ManyToOne | @ManyToOne | ||||
@JsonBackReference | |||||
@JoinColumn(name = "milestoneId") | @JoinColumn(name = "milestoneId") | ||||
open var milestone: Milestone? = null | open var milestone: Milestone? = null | ||||
} | } |
@@ -3,4 +3,5 @@ package com.ffii.tsms.modules.project.entity; | |||||
import com.ffii.core.support.AbstractRepository | import com.ffii.core.support.AbstractRepository | ||||
interface MilestonePaymentRepository : AbstractRepository<MilestonePayment, Long> { | interface MilestonePaymentRepository : AbstractRepository<MilestonePayment, Long> { | ||||
fun findAllByMilestoneIn(milestones: List<Milestone>): List<MilestonePayment> | |||||
} | } |
@@ -1,5 +1,6 @@ | |||||
package com.ffii.tsms.modules.project.entity | package com.ffii.tsms.modules.project.entity | ||||
import com.fasterxml.jackson.annotation.JsonManagedReference | |||||
import com.ffii.core.entity.BaseEntity | import com.ffii.core.entity.BaseEntity | ||||
import com.ffii.tsms.modules.data.entity.* | import com.ffii.tsms.modules.data.entity.* | ||||
import jakarta.persistence.* | import jakarta.persistence.* | ||||
@@ -4,9 +4,8 @@ import com.ffii.core.support.AbstractBaseEntityService | |||||
import com.ffii.core.support.AbstractIdEntityService | import com.ffii.core.support.AbstractIdEntityService | ||||
import com.ffii.core.support.JdbcDao | import com.ffii.core.support.JdbcDao | ||||
import com.ffii.core.utils.PdfUtils | import com.ffii.core.utils.PdfUtils | ||||
import com.ffii.tsms.modules.project.entity.* | |||||
import com.ffii.tsms.modules.project.entity.MilestonePayment | |||||
import com.ffii.tsms.modules.project.entity.MilestonePaymentRepository | |||||
import com.ffii.tsms.modules.project.entity.projections.InvoicePDFReq | import com.ffii.tsms.modules.project.entity.projections.InvoicePDFReq | ||||
import com.ffii.tsms.modules.project.entity.projections.InvoiceSearchInfo | import com.ffii.tsms.modules.project.entity.projections.InvoiceSearchInfo | ||||
import net.sf.jasperreports.engine.JasperCompileManager | import net.sf.jasperreports.engine.JasperCompileManager | ||||
@@ -21,9 +20,18 @@ import java.time.format.DateTimeFormatter | |||||
@Service | @Service | ||||
open class InvoiceService( | open class InvoiceService( | ||||
private val invoiceRepository: InvoiceRepository, | |||||
private val milestoneRepository: MilestoneRepository, | |||||
private val milestonePaymentRepository: MilestonePaymentRepository, | private val milestonePaymentRepository: MilestonePaymentRepository, | ||||
private val jdbcDao: JdbcDao, | private val jdbcDao: JdbcDao, | ||||
) : AbstractIdEntityService<MilestonePayment, Long, MilestonePaymentRepository>(jdbcDao, milestonePaymentRepository){ | |||||
) : AbstractIdEntityService<Invoice, Long, InvoiceRepository>(jdbcDao, invoiceRepository){ | |||||
open fun findAllByProjectAndPaidAmountIsNotNull(project: Project): List<Invoice> { | |||||
val milestones = milestoneRepository.findAllByProject(project) | |||||
val milestonePayments = milestonePaymentRepository.findAllByMilestoneIn(milestones) | |||||
return invoiceRepository.findAllByPaidAmountIsNotNullAndMilestonePaymentIn(milestonePayments) | |||||
} | |||||
open fun allMilestonePayments(): List<Map<String, Any>> { | open fun allMilestonePayments(): List<Map<String, Any>> { | ||||
val sql = StringBuilder(" select " | val sql = StringBuilder(" select " | ||||
+ " mp.id, " | + " mp.id, " | ||||
@@ -0,0 +1,160 @@ | |||||
package com.ffii.tsms.modules.report.service | |||||
import com.ffii.tsms.modules.project.entity.Invoice | |||||
import com.ffii.tsms.modules.project.entity.Project | |||||
import org.apache.poi.ss.usermodel.Sheet | |||||
import org.apache.poi.ss.usermodel.Workbook | |||||
import org.apache.poi.xssf.usermodel.XSSFDataFormat | |||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook | |||||
import org.springframework.core.io.ClassPathResource | |||||
import org.springframework.stereotype.Service | |||||
import java.io.ByteArrayOutputStream | |||||
import java.io.IOException | |||||
import java.time.LocalDate | |||||
import java.time.format.DateTimeFormatter | |||||
@Service | |||||
open class ReportService { | |||||
private val DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd") | |||||
private val FORMATTED_TODAY = LocalDate.now().format(DATE_FORMATTER) | |||||
private val PROJECT_CASH_FLOW_REPORT = "templates/report/EX02_Project Cash Flow Report.xlsx" | |||||
// ==============================|| GENERATE REPORT ||============================== // | |||||
@Throws(IOException::class) | |||||
fun generateProjectCashFlowReport(project: Project, invoices: List<Invoice>): ByteArray { | |||||
// Generate the Excel report with query results | |||||
val workbook: Workbook = createProjectCashFlowReport(project, invoices, PROJECT_CASH_FLOW_REPORT) | |||||
// Write the workbook to a ByteArrayOutputStream | |||||
val outputStream: ByteArrayOutputStream = ByteArrayOutputStream() | |||||
workbook.write(outputStream) | |||||
workbook.close() | |||||
return outputStream.toByteArray() | |||||
} | |||||
// ==============================|| CREATE REPORT ||============================== // | |||||
@Throws(IOException::class) | |||||
private fun createProjectCashFlowReport( | |||||
project: Project, | |||||
invoices: List<Invoice>, | |||||
templatePath: String, | |||||
): Workbook { | |||||
// please create a new function for each report template | |||||
val resource = ClassPathResource(templatePath) | |||||
val templateInputStream = resource.inputStream | |||||
val workbook: Workbook = XSSFWorkbook(templateInputStream) | |||||
val sheet: Sheet = workbook.getSheetAt(0) | |||||
// accounting style + comma style | |||||
val accountingStyle = workbook.createDataFormat().getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)") | |||||
var rowIndex = 1 // Assuming the location is in (1,2), which is the report date field | |||||
var columnIndex = 2 | |||||
sheet.getRow(rowIndex).getCell(columnIndex).apply { | |||||
setCellValue(FORMATTED_TODAY) | |||||
} | |||||
rowIndex = 2 | |||||
sheet.getRow(rowIndex).getCell(columnIndex).apply { | |||||
setCellValue(project.code) | |||||
} | |||||
rowIndex = 3 | |||||
sheet.getRow(rowIndex).getCell(columnIndex).apply { | |||||
setCellValue(project.name) | |||||
} | |||||
rowIndex = 4 | |||||
sheet.getRow(rowIndex).getCell(columnIndex).apply { | |||||
setCellValue(if (project.customer?.name == null) "N/A" else project.customer?.name) | |||||
} | |||||
rowIndex = 5 | |||||
sheet.getRow(rowIndex).getCell(columnIndex).apply { | |||||
setCellValue(if (project.teamLead?.team?.name == null) "N/A" else project.teamLead?.team?.name) | |||||
} | |||||
rowIndex = 9 | |||||
sheet.getRow(rowIndex).apply { | |||||
getCell(1).apply { | |||||
setCellValue(project.expectedTotalFee!!) | |||||
cellStyle.dataFormat = accountingStyle | |||||
} | |||||
getCell(2).apply { | |||||
setCellValue(project.expectedTotalFee!! / 0.8) | |||||
cellStyle.dataFormat = accountingStyle | |||||
} | |||||
} | |||||
rowIndex = 10 | |||||
val actualIncome = invoices.sumOf { invoice -> invoice.paidAmount!! } | |||||
sheet.getRow(rowIndex).apply { | |||||
getCell(1).apply { | |||||
// TODO: Replace by actual expenditure | |||||
setCellValue(actualIncome * 0.8) | |||||
cellStyle.dataFormat = accountingStyle | |||||
} | |||||
getCell(2).apply { | |||||
setCellValue(actualIncome) | |||||
cellStyle.dataFormat = accountingStyle | |||||
} | |||||
} | |||||
rowIndex = 11 | |||||
sheet.getRow(rowIndex).apply { | |||||
getCell(1).apply { | |||||
// TODO: Replace by actual expenditure | |||||
cellFormula = "B10-B11" | |||||
cellStyle.dataFormat = accountingStyle | |||||
} | |||||
getCell(2).apply { | |||||
cellFormula = "C10-C11" | |||||
cellStyle.dataFormat = accountingStyle | |||||
} | |||||
} | |||||
// TODO: Add expenditure | |||||
// formula =IF(B17>0,D16-B17,D16+C17) | |||||
rowIndex = 15 | |||||
val dateFormatter = DateTimeFormatter.ofPattern("MMM YYYY") | |||||
invoices.forEach { invoice: Invoice -> | |||||
sheet.getRow(rowIndex++).apply { | |||||
getCell(0).apply { | |||||
setCellValue(invoice.receiptDate!!.format(dateFormatter)) | |||||
} | |||||
getCell(1).apply { | |||||
setCellValue(0.0) | |||||
cellStyle.dataFormat = accountingStyle | |||||
} | |||||
getCell(2).apply { | |||||
setCellValue(invoice.paidAmount!!) | |||||
cellStyle.dataFormat = accountingStyle | |||||
} | |||||
getCell(3).apply { | |||||
val lastRow = rowIndex - 1 | |||||
if (lastRow == 15) { | |||||
cellFormula = "C{currentRow}".replace("{currentRow}", rowIndex.toString()) | |||||
} else { | |||||
cellFormula = "IF(B{currentRow}>0,D{lastRow}-B{currentRow},D{lastRow}+C{currentRow})".replace("{currentRow}", rowIndex.toString()).replace("{lastRow}", lastRow.toString()) | |||||
} | |||||
cellStyle.dataFormat = accountingStyle | |||||
} | |||||
getCell(4).apply { | |||||
setCellValue(invoice.milestonePayment!!.description!!) | |||||
} | |||||
} | |||||
} | |||||
return workbook | |||||
} | |||||
} |
@@ -1,36 +1,46 @@ | |||||
package com.ffii.tsms.modules.report.web | package com.ffii.tsms.modules.report.web | ||||
import com.ffii.tsms.modules.common.service.ExcelReportService | |||||
import com.ffii.tsms.modules.project.entity.ProjectRepository | |||||
import com.ffii.tsms.modules.report.web.model.EX02ProjectCashFlowReportRequest | |||||
import com.ffii.tsms.modules.project.entity.* | |||||
import com.ffii.tsms.modules.report.service.ReportService | |||||
import com.ffii.tsms.modules.project.service.InvoiceService | |||||
import com.ffii.tsms.modules.report.web.model.ProjectCashFlowReportRequest | |||||
import jakarta.validation.Valid | import jakarta.validation.Valid | ||||
import org.springframework.core.io.ByteArrayResource | import org.springframework.core.io.ByteArrayResource | ||||
import org.springframework.core.io.Resource | import org.springframework.core.io.Resource | ||||
import org.springframework.http.MediaType | |||||
import org.springframework.http.ResponseEntity | import org.springframework.http.ResponseEntity | ||||
import org.springframework.web.bind.ServletRequestBindingException | import org.springframework.web.bind.ServletRequestBindingException | ||||
import org.springframework.web.bind.annotation.GetMapping | |||||
import org.springframework.web.bind.annotation.PathVariable | |||||
import org.springframework.web.bind.annotation.PostMapping | import org.springframework.web.bind.annotation.PostMapping | ||||
import org.springframework.web.bind.annotation.RequestBody | import org.springframework.web.bind.annotation.RequestBody | ||||
import org.springframework.web.bind.annotation.RequestMapping | import org.springframework.web.bind.annotation.RequestMapping | ||||
import org.springframework.web.bind.annotation.RequestParam | |||||
import org.springframework.web.bind.annotation.RestController | import org.springframework.web.bind.annotation.RestController | ||||
import java.io.IOException | import java.io.IOException | ||||
import java.time.LocalDate | import java.time.LocalDate | ||||
@RestController | @RestController | ||||
@RequestMapping("/reports") | @RequestMapping("/reports") | ||||
class ReportController(private val excelReportService: ExcelReportService, private val projectRepository: ProjectRepository) { | |||||
class ReportController(private val invoiceRepository: InvoiceRepository, private val milestonePaymentRepository: MilestonePaymentRepository, private val excelReportService: ReportService, private val projectRepository: ProjectRepository, private val invoiceService: InvoiceService) { | |||||
@PostMapping("/EX02-ProjectCashFlowReport") | |||||
@PostMapping("/ProjectCashFlowReport") | |||||
@Throws(ServletRequestBindingException::class, IOException::class) | @Throws(ServletRequestBindingException::class, IOException::class) | ||||
fun getEx02ProjectCashFlowReport(@RequestBody @Valid request: EX02ProjectCashFlowReportRequest): ResponseEntity<Resource> { | |||||
fun getProjectCashFlowReport(@RequestBody @Valid request: ProjectCashFlowReportRequest): ResponseEntity<Resource> { | |||||
val project = projectRepository.findById(request.projectId).orElseThrow() | val project = projectRepository.findById(request.projectId).orElseThrow() | ||||
val invoices = invoiceService.findAllByProjectAndPaidAmountIsNotNull(project) | |||||
val reportResult: ByteArray = excelReportService.generateEX02ProjectCashFlowReport(project) | |||||
val reportResult: ByteArray = excelReportService.generateProjectCashFlowReport(project, invoices) | |||||
// val mediaType: MediaType = MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") | // val mediaType: MediaType = MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") | ||||
return ResponseEntity.ok() | return ResponseEntity.ok() | ||||
// .contentType(mediaType) | // .contentType(mediaType) | ||||
.header("filename", "EX02 - Project Cash Flow Report - " + LocalDate.now() + ".xlsx") | |||||
.header("filename", "Project Cash Flow Report - " + LocalDate.now() + ".xlsx") | |||||
.body(ByteArrayResource(reportResult)) | .body(ByteArrayResource(reportResult)) | ||||
} | } | ||||
@GetMapping("/test/{id}") | |||||
fun test(@PathVariable id: Long): List<Invoice> { | |||||
val project = projectRepository.findById(id).orElseThrow() | |||||
return invoiceService.findAllByProjectAndPaidAmountIsNotNull(project) | |||||
} | |||||
} | } |
@@ -1,5 +1,5 @@ | |||||
package com.ffii.tsms.modules.report.web.model | package com.ffii.tsms.modules.report.web.model | ||||
data class EX02ProjectCashFlowReportRequest ( | |||||
data class ProjectCashFlowReportRequest ( | |||||
val projectId: Long | val projectId: Long | ||||
) | ) |
@@ -0,0 +1,39 @@ | |||||
-- liquibase formatted sql | |||||
-- changeset cyril:create invoice | |||||
CREATE TABLE `invoice` ( | |||||
`id` INT NOT NULL AUTO_INCREMENT, | |||||
`version` INT NOT NULL DEFAULT '0', | |||||
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, | |||||
`createdBy` VARCHAR(30) NULL DEFAULT NULL, | |||||
`modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, | |||||
`modifiedBy` VARCHAR(30) NULL DEFAULT NULL, | |||||
`deleted` TINYINT(1) NOT NULL DEFAULT '0', | |||||
`invoiceNo` VARCHAR(45) NOT NULL, | |||||
`projectId` INT NOT NULL, | |||||
`milestonePaymentId` INT NOT NULL, | |||||
`clientId` INT NOT NULL, | |||||
`invoiceDate` DATE NOT NULL, | |||||
`dueDate` DATE NOT NULL, | |||||
`receiptDate` DATE NULL, | |||||
`unpaidAmount` DECIMAL(16,2) NOT NULL, | |||||
`paidAmount` DECIMAL(16,2) NULL, | |||||
PRIMARY KEY (`id`), | |||||
INDEX `FK_INVOICE_ON_PROJECTID` (`projectId` ASC) VISIBLE, | |||||
INDEX `FK_INVOICE_ON_MILESTONEPAYMENTID` (`milestonePaymentId` ASC) VISIBLE, | |||||
INDEX `FK_INVOICE_ON_CLIENTID` (`clientId` ASC) VISIBLE, | |||||
CONSTRAINT `FK_INVOICE_ON_PROJECTID` | |||||
FOREIGN KEY (`projectId`) | |||||
REFERENCES `project` (`id`) | |||||
ON DELETE NO ACTION | |||||
ON UPDATE NO ACTION, | |||||
CONSTRAINT `FK_INVOICE_ON_MILESTONEPAYMENTID` | |||||
FOREIGN KEY (`milestonePaymentId`) | |||||
REFERENCES `milestone_payment` (`id`) | |||||
ON DELETE NO ACTION | |||||
ON UPDATE NO ACTION, | |||||
CONSTRAINT `FK_INVOICE_ON_CLIENTID` | |||||
FOREIGN KEY (`clientId`) | |||||
REFERENCES `customer` (`id`) | |||||
ON DELETE NO ACTION | |||||
ON UPDATE NO ACTION); |
@@ -0,0 +1,9 @@ | |||||
-- liquibase formatted sql | |||||
-- changeset cyril:update invoice | |||||
ALTER TABLE `invoice` | |||||
DROP FOREIGN KEY `FK_INVOICE_ON_CLIENTID`; | |||||
ALTER TABLE `invoice` | |||||
DROP COLUMN `clientId`, | |||||
DROP INDEX `FK_INVOICE_ON_CLIENTID` ; | |||||
; |
@@ -0,0 +1,9 @@ | |||||
-- liquibase formatted sql | |||||
-- changeset cyril:update invoice | |||||
ALTER TABLE `invoice` | |||||
DROP FOREIGN KEY `FK_INVOICE_ON_PROJECTID`; | |||||
ALTER TABLE `invoice` | |||||
DROP COLUMN `projectId`, | |||||
DROP INDEX `FK_INVOICE_ON_PROJECTID` ; | |||||
; |