Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add customer notes #475

Merged
merged 52 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
5b5a41d
added db
stephanprantl Mar 19, 2023
e7707a5
added api
stephanprantl Mar 19, 2023
f9ed9b3
added api client
stephanprantl Mar 19, 2023
bbdc411
refactored
stephanprantl Mar 19, 2023
88af6cd
Merge branch 'main' into add-notes
stephanprantl Mar 22, 2023
5b31856
basic note panel
stephanprantl Mar 22, 2023
14bf128
added tabs
stephanprantl Mar 22, 2023
b788722
improved layout
stephanprantl Mar 22, 2023
21586ce
improved layout
stephanprantl Mar 22, 2023
964aae6
improved layout
stephanprantl Mar 22, 2023
62ab632
improved layout
stephanprantl Mar 22, 2023
777653a
improved layout
stephanprantl Mar 22, 2023
5e9af12
improved layout
stephanprantl Mar 22, 2023
e8506a1
improved layout
stephanprantl Mar 22, 2023
d00defd
improved layout
stephanprantl Mar 22, 2023
9a0746f
improved layout
stephanprantl Mar 22, 2023
38937b0
basic note added
stephanprantl Mar 22, 2023
768002d
renamed
stephanprantl Mar 22, 2023
0514008
added space
stephanprantl Mar 22, 2023
3eac995
renamed
stephanprantl Mar 22, 2023
3c473a0
fixed test
stephanprantl Mar 22, 2023
3560eb7
added expects
stephanprantl Mar 22, 2023
82d08b1
fixed test
stephanprantl Mar 22, 2023
bb06c69
added controller test
stephanprantl Mar 22, 2023
baf7eb9
added service test
stephanprantl Mar 22, 2023
bcf1546
splitted file
stephanprantl Mar 22, 2023
32372ce
added buttons
stephanprantl Mar 22, 2023
6c39b3c
added showall modal
stephanprantl Mar 22, 2023
76d7a8b
added new modal
stephanprantl Mar 22, 2023
fe94e94
added backend
stephanprantl Mar 22, 2023
1816e56
added frontend
stephanprantl Mar 22, 2023
dbf7764
cleanup
stephanprantl Mar 22, 2023
27da7a3
switched to entity
stephanprantl Mar 22, 2023
b19e4c4
added tests
stephanprantl Mar 22, 2023
d7f7f1d
fixed test
stephanprantl Mar 22, 2023
6b21bcc
added test
stephanprantl Mar 22, 2023
c5a279c
fixed linebreaks
stephanprantl Mar 22, 2023
4cda88c
fixed modal
stephanprantl Mar 22, 2023
9204369
adapted testdata
stephanprantl Mar 22, 2023
2fb71ac
fixed lint msg
stephanprantl Mar 22, 2023
f42da14
removed log
stephanprantl Mar 23, 2023
1f9b067
fixed menu
stephanprantl Mar 23, 2023
a1d8496
added todo
stephanprantl Mar 23, 2023
3569ee2
refined and added note to checkin
stephanprantl Mar 23, 2023
12516ea
cleanup
stephanprantl Mar 23, 2023
c42af62
cleanup
stephanprantl Mar 23, 2023
66067b0
added test
stephanprantl Mar 23, 2023
be99647
adapted badge size
stephanprantl Mar 23, 2023
52c12a3
added employer addPersons to pdf
stephanprantl Mar 23, 2023
c444155
stricter edit
stephanprantl Mar 23, 2023
04b0e27
fixed linux references
stephanprantl Mar 23, 2023
c21dae7
removed todo
stephanprantl Mar 23, 2023
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* General
* Unify errormsg handling (panel on top) ?
* Toast messages?
* customer creation
* add employer field also for additional persons
* customer-detail
* add document upload
* Fix autofocus: https://davidmcintosh.medium.com/auto-focusing-an-angular-input-the-easy-way-part-1-dcb1799e025f

### Open things (to be discussed)
Expand Down
11 changes: 11 additions & 0 deletions _http-calls/customer-notes.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
### Get notes
GET {{baseApiPath}}/customers/101/notes
Content-Type: application/json

### Create new note
POST {{baseApiPath}}/customers/101/notes
Content-Type: application/json

{
"note": "new dummy note"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package at.wrk.tafel.admin.backend.database.entities.customer

import at.wrk.tafel.admin.backend.common.ExcludeFromTestCoverage
import at.wrk.tafel.admin.backend.database.entities.auth.UserEntity
import at.wrk.tafel.admin.backend.database.entities.base.BaseChangeTrackingEntity
import jakarta.persistence.*

@Entity(name = "CustomerNote")
@Table(name = "customers_notes")
@ExcludeFromTestCoverage
class CustomerNoteEntity : BaseChangeTrackingEntity() {

@ManyToOne
@JoinColumn(name = "customer_id", nullable = false)
var customer: CustomerEntity? = null

@ManyToOne
@JoinColumn(name = "user_id", nullable = true)
var user: UserEntity? = null

@Column(name = "note")
var note: String? = null

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package at.wrk.tafel.admin.backend.database.repositories.customer

import at.wrk.tafel.admin.backend.database.entities.customer.CustomerNoteEntity
import org.springframework.data.jpa.repository.JpaRepository

interface CustomerNoteRepository : JpaRepository<CustomerNoteEntity, Long> {
fun findByCustomerIdOrderByCreatedAtDesc(customerId: Long): List<CustomerNoteEntity>

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ class CustomerController(
return service.createCustomer(customer)
}

@PostMapping("/{id}")
@PostMapping("/{customerId}")
fun updateCustomer(
@PathVariable("id") customerId: Long,
@PathVariable("customerId") customerId: Long,
@RequestBody customer: Customer
): Customer {
if (!service.existsByCustomerId(customerId)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class CustomerPdfServiceImpl : CustomerPdfService {
firstname = it.firstname!!,
birthDate = it.birthDate!!.format(DATE_FORMATTER),
country = it.country!!.name!!,
employer = it.employer ?: "-",
income = it.income
?.takeIf { income -> income.compareTo(BigDecimal.ZERO) != 0 }
?.let { income ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ data class PdfAdditionalPersonData(
val firstname: String,
val birthDate: String,
val country: String,
val employer: String? = null,
val income: String? = null,
val incomeDueDate: String? = null
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package at.wrk.tafel.admin.backend.modules.customer.note

import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/api/customers/{customerId}/notes")
@PreAuthorize("hasAuthority('CUSTOMER')")
class CustomerNoteController(
private val service: CustomerNoteService
) {

@GetMapping
fun getNotes(@PathVariable("customerId") customerId: Long): CustomerNotesResponse {
val notes = service.getNotes(customerId)
return CustomerNotesResponse(notes = notes)
}

@PostMapping
fun createNewNote(
@PathVariable("customerId") customerId: Long,
@RequestBody request: CreateCustomerNoteRequest
): ResponseEntity<CustomerNoteItem> {
if (request.note.isNullOrBlank()) {
return ResponseEntity.badRequest().build()
}

val newNote = service.createNewNote(customerId, request.note)
return ResponseEntity.ok(newNote)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package at.wrk.tafel.admin.backend.modules.customer.note

import at.wrk.tafel.admin.backend.common.auth.model.TafelJwtAuthentication
import at.wrk.tafel.admin.backend.database.entities.customer.CustomerNoteEntity
import at.wrk.tafel.admin.backend.database.repositories.auth.UserRepository
import at.wrk.tafel.admin.backend.database.repositories.customer.CustomerNoteRepository
import at.wrk.tafel.admin.backend.database.repositories.customer.CustomerRepository
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.stereotype.Service

@Service
class CustomerNoteService(
private val customerNoteRepository: CustomerNoteRepository,
private val customerRepository: CustomerRepository,
private val userRepository: UserRepository
) {

fun getNotes(customerId: Long): List<CustomerNoteItem> {
val notes = customerNoteRepository.findByCustomerIdOrderByCreatedAtDesc(customerId)
return notes.map { mapNote(it) }
}

private fun mapNote(entity: CustomerNoteEntity): CustomerNoteItem {
val user = entity.user
val userDisplayString = listOfNotNull(user?.personnelNumber, user?.firstname, user?.lastname)
.joinToString(" ")
.ifBlank { null }

return CustomerNoteItem(
author = userDisplayString,
timestamp = entity.createdAt!!,
note = entity.note!!
)
}

fun createNewNote(customerId: Long, note: String): CustomerNoteItem {
val authenticatedUser = SecurityContextHolder.getContext().authentication as TafelJwtAuthentication

val noteEntity = CustomerNoteEntity()
noteEntity.customer = customerRepository.findByCustomerId(customerId).get()
noteEntity.user = userRepository.findByUsername(authenticatedUser.username!!).get()
noteEntity.note = note

val savedEntity = customerNoteRepository.save(noteEntity)
return mapNote(savedEntity)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package at.wrk.tafel.admin.backend.modules.customer.note

import at.wrk.tafel.admin.backend.common.ExcludeFromTestCoverage
import java.time.ZonedDateTime

@ExcludeFromTestCoverage
data class CustomerNotesResponse(
val notes: List<CustomerNoteItem> = emptyList()
)

@ExcludeFromTestCoverage
data class CustomerNoteItem(
val author: String? = null,
val timestamp: ZonedDateTime,
val note: String
)

@ExcludeFromTestCoverage
data class CreateCustomerNoteRequest(
val note: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class DistributionService(

val distribution = DistributionEntity()
distribution.startedAt = ZonedDateTime.now()
distribution.startedByUser = userRepository.findByUsername(authenticatedUser.username!!).orElse(null)
distribution.startedByUser = userRepository.findByUsername(authenticatedUser.username!!).get()
distribution.state = DistributionState.OPEN

return distributionRepository.save(distribution)
Expand Down
6 changes: 6 additions & 0 deletions backend/src/main/resources/db-migration-testdata/testdata.sql
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ values (1012, NOW(), NOW(), 101, 'Child 2', 'Musterfrau', CURRENT_DATE - interva
INSERT INTO customers_addpersons (id, created_at, updated_at, customer_id, firstname, lastname, birth_date, employer, income,
income_due, country_id)
values (1013, NOW(), NOW(), 101, 'Child 3', 'Musterfrau', CURRENT_DATE - interval '2 year', 'WRK', null, null, 1);
INSERT INTO customers_notes (id, created_at, updated_at, customer_id, user_id, note)
VALUES (1003, NOW(), NOW(), 101, 100, 'Testnote 3.<br/>Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.<br/><br/>Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.');
INSERT INTO customers_notes (id, created_at, updated_at, customer_id, user_id, note)
VALUES (1002, NOW(), NOW(), 101, 100, 'Testnote 2');
INSERT INTO customers_notes (id, created_at, updated_at, customer_id, user_id, note)
VALUES (1001, NOW(), NOW(), 101, null, 'Testnote 1');

INSERT INTO customers (id, created_at, updated_at, customer_id, user_id, firstname, lastname, birth_date, country_id,
address_street, address_houseNumber, address_stairway, address_door, address_postalCode,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
create table if not exists customers_notes
(
id bigint primary key,
created_at timestamp not null,
updated_at timestamp not null,
customer_id bigint not null REFERENCES customers (id) ON DELETE CASCADE,
user_id bigint null REFERENCES users (id) ON DELETE SET NULL,
note text not null
);
10 changes: 10 additions & 0 deletions backend/src/main/resources/pdf-templates/includes/masterdata.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,16 @@
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell>
<fo:block>Arbeitgeber:</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>
<xsl:value-of select="./employer"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell>
<fo:block>Einkommen:</fo:block>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package at.wrk.tafel.admin.backend.modules.customer.note

import io.mockk.every
import io.mockk.impl.annotations.InjectMockKs
import io.mockk.impl.annotations.RelaxedMockK
import io.mockk.junit5.MockKExtension
import io.mockk.verify
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.http.HttpStatus
import java.time.ZonedDateTime

@ExtendWith(MockKExtension::class)
internal class CustomerNoteControllerTest {

@RelaxedMockK
private lateinit var service: CustomerNoteService

@InjectMockKs
private lateinit var controller: CustomerNoteController

@Test
fun `get notes - empty`() {
val customerId = 123L
every { service.getNotes(customerId) } returns emptyList()

val response = controller.getNotes(customerId)

assertThat(response.notes).isEmpty()
}

@Test
fun `get notes - found`() {
val customerId = 123L
val notes = listOf(
CustomerNoteItem(
author = "author 2",
timestamp = ZonedDateTime.now().minusDays(1),
note = "note 2"
),
CustomerNoteItem(
author = "author 1",
timestamp = ZonedDateTime.now().minusDays(2),
note = "note 1"
)
)
every { service.getNotes(customerId) } returns notes

val response = controller.getNotes(customerId)

assertThat(response.notes).hasSize(notes.size)
assertThat(response.notes).isEqualTo(notes)
verify { service.getNotes(customerId) }
}

@Test
fun `create new note - empty text`() {
val customerId = 123L

val response = controller.createNewNote(
customerId = customerId,
request = CreateCustomerNoteRequest(note = "")
)

assertThat(response.statusCode).isEqualTo(HttpStatus.BAD_REQUEST)
}

@Test
fun `create new note - successful`() {
val customerId = 123L
val note = "test note"

val noteItem = CustomerNoteItem(
author = "author 2",
timestamp = ZonedDateTime.now().minusDays(1),
note = "note 2"
)
every { service.createNewNote(customerId, note) } returns noteItem

val response = controller.createNewNote(
customerId = customerId,
request = CreateCustomerNoteRequest(note = note)
)

assertThat(response.statusCode).isEqualTo(HttpStatus.OK)
assertThat(response.body).isEqualTo(noteItem)
verify { service.createNewNote(customerId, note) }
}

}
Loading