diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php index f59fc567e..edf94bd67 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php +++ b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php @@ -99,6 +99,22 @@ class Household return $this; } + /** + * Force an address starting at the current day + * on the Household. + * + * This will force the startDate's address on today. + * + * Used on household creation. + * + * @Serializer\Groups({"create"}) + */ + public function setForceAddress(Address $address) + { + $address->setValidFrom(new \DateTime('today')); + $this->addAddress($address); + } + /** * @param Address $address */ @@ -128,7 +144,7 @@ class Household $at = $at === null ? new \DateTime('today') : $at; $addrs = $this->getAddresses()->filter(function (Address $a) use ($at) { - return $a->getValidFrom() < $at && ( + return $a->getValidFrom() <= $at && ( NULL === $a->getValidTo() || $at < $a->getValidTo() ); }); diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/< b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/< new file mode 100644 index 000000000..f2917b463 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/< @@ -0,0 +1,374 @@ +import { createStore } from 'vuex'; +import { householdMove, fetchHouseholdSuggestionByAccompanyingPeriod, fetchAddressSuggestionByPerson} from './../api.js'; +import { datetimeToISO } from 'ChillMainAssets/chill/js/date.js'; + +const debug = process.env.NODE_ENV !== 'production'; + +const concerned = window.household_members_editor_data.persons.map(p => { + return { + person: p, + position: null, + allowRemove: false, + holder: false, + comment: "", + }; +}); + +console.log('expand suggestions', window.household_members_editor_expand_suggestions === 1); + +const store = createStore({ + strict: debug, + state: { + concerned, + household: window.household_members_editor_data.household, + positions: window.household_members_editor_data.positions.sort((a, b) => { + if (a.ordering < b.ordering) { + return -1; + } + if (a.ordering > b.ordering) { + return 1; + } + return 0; + }), + startDate: new Date(), + allowHouseholdCreate: window.household_members_editor_data.allowHouseholdCreate, + allowHouseholdSearch: window.household_members_editor_data.allowHouseholdSearch, + allowLeaveWithoutHousehold: window.household_members_editor_data.allowLeaveWithoutHousehold, + forceLeaveWithoutHousehold: false, + householdSuggestionByAccompanyingPeriod: [], + showHouseholdSuggestion: window.household_members_editor_expand_suggestions === 1, + addressesSuggestion: [], + warnings: [], + errors: [] + }, + getters: { + isHouseholdNew(state) { + if (state.household === null) { + return false; + } + return !Number.isInteger(state.household.id); + }, + hasHousehold(state) { + return state.household !== null; + }, + hasHouseholdOrLeave(state) { + return state.household !== null || state.forceLeaveWithoutHousehold; + }, + hasHouseholdSuggestion(state, getters) { + return getters.filterHouseholdSuggestionByAccompanyingPeriod.length > 0; + }, + countHouseholdSuggestion(state, getters) { + return getters.filterHouseholdSuggestionByAccompanyingPeriod.length; + }, + filterHouseholdSuggestionByAccompanyingPeriod(state) { + if (state.household === null) { + return state.householdSuggestionByAccompanyingPeriod; + } + + return state.householdSuggestionByAccompanyingPeriod + .filter(h => h.id !== state.household.id) + ; + }, + hasPersonsWellPositionnated(state, getters) { + return getters.needsPositionning === false + || (getters.persons.length > 0 && getters.concUnpositionned.length === 0); + }, + persons(state) { + return state.concerned.map(conc => conc.person); + }, + concUnpositionned(state) { + return state.concerned + .filter(conc => conc.position === null) + ; + }, + positions(state) { + return state.positions; + }, + personByPosition: (state) => (position_id) => { + return state.concerned + .filter(conc => + conc.position !== null ? conc.position.id === position_id : false + ) + .map(conc => conc.person) + ; + }, + concByPosition: (state) => (position_id) => { + return state.concerned + .filter(conc => + conc.position !== null ? conc.position.id === position_id : false + ) + ; + }, + concByPersonId: (state) => (person_id) => { + return state.concerned + .find(conc => conc.person.id === person_id) + ; + }, + needsPositionning(state) { + return state.forceLeaveWithoutHousehold === false; + }, + buildPayload: (state) => { + let + conc, + payload_conc, + payload = { + concerned: [], + destination: null + } + ; + + if (state.forceLeaveWithoutHousehold === false) { + payload.destination = { + id: state.household.id, + type: state.household.type + }; + } + + for (let i in state.concerned) { + conc = state.concerned[i]; + payload_conc = { + person: { + id: conc.person.id, + type: conc.person.type + }, + start_date: { + datetime: datetimeToISO(state.startDate) + } + }; + + if (state.forceLeaveWithoutHousehold === false) { + payload_conc.position = { + id: conc.position.id, + type: conc.position.type + }; + payload_conc.holder = conc.holder; + payload_conc.comment = conc.comment; + } + + payload.concerned.push(payload_conc); + } + + return payload; + }, + }, + mutations: { + addConcerned(state, person) { + let persons = state.concerned.map(conc => conc.person.id); + if (!persons.includes(person.id)) { + state.concerned.push({ + person, + position: null, + allowRemove: true, + holder: false, + comment: "", + }); + } else { + console.err("person already included"); + } + }, + markPosition(state, { person_id, position_id}) { + let + position = state.positions.find(pos => pos.id === position_id), + conc = state.concerned.find(c => c.person.id === person_id); + conc.position = position; + }, + setComment(state, {conc, comment}) { + conc.comment = comment; + }, + toggleHolder(state, conc) { + conc.holder = !conc.holder; + }, + removePosition(state, conc) { + conc.holder = false; + conc.position = null; + }, + removeConcerned(state, conc) { + state.concerned = state.concerned.filter(c => + c.person.id !== conc.person.id + ) + }, + createHousehold(state) { + state.household = { type: 'household', members: [], current_address: null, + current_members_id: [] }; + state.forceLeaveWithoutHousehold = false; + }, + removeHousehold(state) { + state.household = null; + state.forceLeaveWithoutHousehold = false; + }, + setHouseholdAddress(state, address) { + if (null === state.household) { + console.error("no household"); + throw new Error("No household"); + } + + state.household.current_address = address; + state.household.force_new_address = address; + }, + forceLeaveWithoutHousehold(state) { + state.household = null; + state.forceLeaveWithoutHousehold = true; + }, + selectHousehold(state, household) { + state.household = household; + state.forceLeaveWithoutHousehold = false; + }, + setHouseholdSuggestionByAccompanyingPeriod(state, households) { + let existingIds = state.householdSuggestionByAccompanyingPeriod + .map(h => h.id); + for (let i in households) { + if (!existingIds.includes(households[i].id)) { + state.householdSuggestionByAccompanyingPeriod.push(households[i]); + } + } + }, + setStartDate(state, dateI) { + state.startDate = dateI; + }, + toggleHouseholdSuggestion(state) { + state.showHouseholdSuggestion = !state.showHouseholdSuggestion; + }, + setWarnings(state, warnings) { + state.warnings = warnings; + // reset errors, which should come from servers + state.errors.splice(0, state.errors.length); + }, + setErrors(state, errors) { + state.errors = errors; + }, + addAddressesSuggestion(state, addresses) { + let existingIds = state.addressesSuggestion + .map(a => a.id); + + for (let i in addresses) { + if (!existingIds.includes(addresses[i].id)) { + state.addressesSuggestion.push(addresses[i]); + } + } + } + }, + actions: { + addConcerned({ commit, dispatch }, person) { + commit('addConcerned', person); + dispatch('computeWarnings'); + dispatch('fetchAddressSuggestions'); + }, + markPosition({ commit, state, dispatch }, { person_id, position_id }) { + commit('markPosition', { person_id, position_id }); + dispatch('computeWarnings'); + }, + toggleHolder({ commit, dispatch }, conc) { + commit('toggleHolder', conc); + dispatch('computeWarnings'); + }, + removePosition({ commit, dispatch }, conc) { + commit('removePosition', conc); + dispatch('computeWarnings'); + }, + removeConcerned({ commit, dispatch }, conc) { + commit('removeConcerned', conc); + dispatch('computeWarnings'); + dispatch('fetchAddressSuggestions'); + }, + removeHousehold({ commit, dispatch }) { + commit('removeHousehold'); + dispatch('computeWarnings'); + }, + createHousehold({ commit, dispatch }) { + commit('createHousehold'); + dispatch('computeWarnings'); + }, + forceLeaveWithoutHousehold({ commit, dispatch }) { + commit('forceLeaveWithoutHousehold'); + dispatch('computeWarnings'); + }, + selectHousehold({ commit }, h) { + commit('selectHousehold', h); + dispatch('computeWarnings'); + }, + setStartDate({ commit, dispatch }, date) { + commit('setStartDate', date); + dispatch('computeWarnings'); + }, + setComment({ commit }, payload) { + commit('setComment', payload); + }, + fetchHouseholdSuggestionForConcerned({ commit, state }, person) { + fetchHouseholdSuggestionByAccompanyingPeriod(person.id) + .then(households => { + commit('setHouseholdSuggestionByAccompanyingPeriod', households); + }); + }, + fetchAddressSuggestions({ commit, state }) { + for (let i in state.concerned) { + fetchAddressSuggestionByPerson(state.concerned[i].person.id) + .then(addresses => { + commit('addAddressesSuggestion', addresses); + }) + .catch(e => { + console.log(e); + }); + } + }, + computeWarnings({ commit, state, getters }) { + let warnings = [], + payload; + + if (!getters.hasHousehold && !state.forceLeaveWithoutHousehold) { + warnings.push({ m: 'household_members_editor.add_destination', a: {} }); + } + + if (state.concerned.length === 0) { + warnings.push({ m: 'household_members_editor.add_at_least_onePerson', a: {} }); + } + + if (getters.concUnpositionned.length > 0 + && !state.forceLeaveWithoutHousehold) { + warnings.push({ m: 'household_members_editor.give_a_position_to_every_person', a: {} }) + } + + commit('setWarnings', warnings); + }, + confirm({ getters, state, commit }) { + let payload = getters.buildPayload, + errors = [], + person_id, + household_id, + error + ; + + householdMove(payload).then(household => { + if (household === null) { + person_id = getters.persons[0].id; + window.location.replace(`/fr/person/${person_id}/general`); + } else { + if (household.type === 'household') { + household_id = household.id; + // nothing to do anymore here, bye-bye ! + window.location.replace(`/fr/person/household/${household_id}/summary`); + } else { + // we assume the answer was 422... + error = household; + for (let i in error.violations) { + let e = error.violations[i]; + errors.push(e.title); + } + + commit('setErrors', errors); + } + } + }); + }, + } +}); + +store.dispatch('computeWarnings'); +store.dispatch('fetchAddressSuggestions'); + +if (concerned.length > 0) { + concerned.forEach(c => { + store.dispatch('fetchHouseholdSuggestionForConcerned', c.person); + }); +} + +export { store }; diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/api.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/api.js index e54bd9b15..7a91ec912 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/api.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/api.js @@ -37,7 +37,20 @@ const fetchHouseholdSuggestionByAccompanyingPeriod = (personId) => { ; }; +const fetchAddressSuggestionByPerson = (personId) => { + const url = `/api/1.0/person/address/suggest/by-person/${personId}.json`; + return window.fetch(url) + .then(response => { + if (response.ok) { + return response.json(); + } + + throw Error({m: 'Error while fetch address suggestion', status: response.status}); + }); +} + export { householdMove, fetchHouseholdSuggestionByAccompanyingPeriod, + fetchAddressSuggestionByPerson, }; diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Household.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Household.vue index 4c4929581..fa3dd64b6 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Household.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/HouseholdMembersEditor/components/Household.vue @@ -5,6 +5,38 @@
+
+ +

À quelle adresse habite ce ménage ?

+ + + + + +
+
+ + +
{{ $t('household_members_editor.household.will_leave_any_household') }} @@ -23,7 +55,7 @@ countHouseholdSuggestion) }} -
  • +
  • +
  • + +
  • -
    +

    {{ $t('household_members_editor.household_for_participants_accompanying_period') }}:

    h.id !== state.household.id) ; }, + filterAddressesSuggestion(state) { + if (state.household === null) { + return state.addressesSuggestion; + } + + if (state.household.current_address === null) { + return state.addressesSuggestion; + } + + return state.addressesSuggestion + .filter(a => a.address_id !== state.household.current_address.address_id); + }, hasPersonsWellPositionnated(state, getters) { return getters.needsPositionning === false || (getters.persons.length > 0 && getters.concUnpositionned.length === 0); @@ -106,7 +125,7 @@ const store = createStore({ needsPositionning(state) { return state.forceLeaveWithoutHousehold === false; }, - buildPayload: (state) => { + buildPayload: (state, getters) => { let conc, payload_conc, @@ -119,8 +138,13 @@ const store = createStore({ if (state.forceLeaveWithoutHousehold === false) { payload.destination = { id: state.household.id, - type: state.household.type + type: state.household.type, }; + + if (getters.isHouseholdNew && state.household.current_address !== null) { + console.log(state.household); + payload.destination.forceAddress = { id: state.household.current_address.address_id }; + } } for (let i in state.concerned) { @@ -187,9 +211,29 @@ const store = createStore({ ) }, createHousehold(state) { - state.household = { type: 'household', members: [], current_address: null } + state.household = { type: 'household', members: [], current_address: null, current_members_id: [] } state.forceLeaveWithoutHousehold = false; }, + removeHousehold(state) { + state.household = null; + state.forceLeaveWithoutHousehold = false; + }, + setHouseholdAddress(state, address) { + if (null === state.household) { + console.error("no household"); + throw new Error("No household"); + } + + state.household.current_address = address; + }, + removeHouseholdAddress(state, address) { + if (null === state.household) { + console.error("no household"); + throw new Error("No household"); + } + + state.household.current_address = null; + }, forceLeaveWithoutHousehold(state) { state.household = null; state.forceLeaveWithoutHousehold = true; @@ -221,11 +265,22 @@ const store = createStore({ setErrors(state, errors) { state.errors = errors; }, + addAddressesSuggestion(state, addresses) { + let existingIds = state.addressesSuggestion + .map(a => a.address_id); + + for (let i in addresses) { + if (!existingIds.includes(addresses[i].address_id)) { + state.addressesSuggestion.push(addresses[i]); + } + } + } }, actions: { addConcerned({ commit, dispatch }, person) { commit('addConcerned', person); dispatch('computeWarnings'); + dispatch('fetchAddressSuggestions'); }, markPosition({ commit, state, dispatch }, { person_id, position_id }) { commit('markPosition', { person_id, position_id }); @@ -242,6 +297,11 @@ const store = createStore({ removeConcerned({ commit, dispatch }, conc) { commit('removeConcerned', conc); dispatch('computeWarnings'); + dispatch('fetchAddressSuggestions'); + }, + removeHousehold({ commit, dispatch }) { + commit('removeHousehold'); + dispatch('computeWarnings'); }, createHousehold({ commit, dispatch }) { commit('createHousehold'); @@ -268,6 +328,17 @@ const store = createStore({ commit('setHouseholdSuggestionByAccompanyingPeriod', households); }); }, + fetchAddressSuggestions({ commit, state }) { + for (let i in state.concerned) { + fetchAddressSuggestionByPerson(state.concerned[i].person.id) + .then(addresses => { + commit('addAddressesSuggestion', addresses); + }) + .catch(e => { + console.log(e); + }); + } + }, computeWarnings({ commit, state, getters }) { let warnings = [], payload; @@ -321,6 +392,7 @@ const store = createStore({ }); store.dispatch('computeWarnings'); +store.dispatch('fetchAddressSuggestions'); if (concerned.length > 0) { concerned.forEach(c => { diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/MembersEditorNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/MembersEditorNormalizer.php index 57745e276..b2c21c048 100644 --- a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/MembersEditorNormalizer.php +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/MembersEditorNormalizer.php @@ -77,8 +77,11 @@ class MembersEditorNormalizer implements DenormalizerInterface, DenormalizerAwar protected function denormalizeMove($data, string $type, string $format, array $context = []) { + $householdContext = $context; + $householdContext['groups'][] = 'create'; + $household = $this->denormalizer->denormalize($data['destination'], Household::class, - $format, $context); + $format, $householdContext); if (NULL === $household) { throw new Exception\InvalidArgumentException("household could not be denormalized. Impossible to process"); diff --git a/src/Bundle/ChillPersonBundle/chill.api.specs.yaml b/src/Bundle/ChillPersonBundle/chill.api.specs.yaml index 0684a352c..ad4f6109c 100644 --- a/src/Bundle/ChillPersonBundle/chill.api.specs.yaml +++ b/src/Bundle/ChillPersonBundle/chill.api.specs.yaml @@ -1169,14 +1169,32 @@ paths: id: 0 type: person position: - type: position + type: household_position id: 1 start_date: - datetime: 2021-06-01T00:00:00+02:00 + datetime: "2021-06-01T00:00:00+02:00" comment: "This is my comment for moving" holder: false destination: type: household + Moving person to a new household and set an address to this household: + value: + concerned: + - + person: + id: 0 + type: person + position: + type: household_position + id: 1 + start_date: + datetime: "2021-06-01T00:00:00+02:00" + comment: "This is my comment for moving" + holder: false + destination: + type: household + forceAddress: + id: 0 Moving person to an existing household: value: concerned: @@ -1185,7 +1203,7 @@ paths: id: 0 type: person position: - type: position + type: household_position id: 1 start_date: datetime: 2021-06-01T00:00:00+02:00