flamingo.me/flamingo-commerce/v3@v3.11.0/cart/interfaces/controller/forms/personaldataform.go (about)

     1  package forms
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net/url"
     7  	"strconv"
     8  	"time"
     9  
    10  	"flamingo.me/flamingo/v3/framework/config"
    11  	"flamingo.me/flamingo/v3/framework/flamingo"
    12  	"flamingo.me/flamingo/v3/framework/web"
    13  	"flamingo.me/form/application"
    14  	"flamingo.me/form/domain"
    15  	"github.com/go-playground/form/v4"
    16  
    17  	cartApplication "flamingo.me/flamingo-commerce/v3/cart/application"
    18  	"flamingo.me/flamingo-commerce/v3/cart/domain/cart"
    19  	customerApplication "flamingo.me/flamingo-commerce/v3/customer/application"
    20  )
    21  
    22  type (
    23  	// PersonalDataForm - interface for the form DTO
    24  	PersonalDataForm interface {
    25  		MapPerson() *cart.Person
    26  		MapAdditionalData() *cart.AdditionalData
    27  	}
    28  
    29  	// DefaultPersonalDataFormProvider for creating form instances
    30  	DefaultPersonalDataFormProvider func() *DefaultPersonalDataForm
    31  
    32  	// DefaultPersonalDataForm - the standard form dto for Person data (that implements PersonalDataForm)
    33  	DefaultPersonalDataForm struct {
    34  		DateOfBirth             string      `form:"dateOfBirth"`
    35  		PassportCountry         string      `form:"passportCountry"`
    36  		PassportNumber          string      `form:"passportNumber"`
    37  		Address                 AddressForm `form:"address" validate:"-"`
    38  		additionalFormData      map[string]string
    39  		additionalFormFieldsCfg config.Slice
    40  	}
    41  
    42  	// DefaultPersonalDataFormService implements Form(Data)Provider interface of form package
    43  	DefaultPersonalDataFormService struct {
    44  		applicationCartReceiverService  *cartApplication.CartReceiverService
    45  		defaultPersonalDataFormProvider DefaultPersonalDataFormProvider
    46  		customerApplicationService      *customerApplication.Service
    47  		additionalFormFieldsCfg         config.Slice
    48  		dateOfBirthRequired             bool
    49  		minAge                          int
    50  		passportCountryRequired         bool
    51  		passportNumberRequired          bool
    52  	}
    53  
    54  	// PersonalDataFormController - the (mini) MVC for handling Personal Data (Purchaser)
    55  	PersonalDataFormController struct {
    56  		responder                      *web.Responder
    57  		applicationCartService         *cartApplication.CartService
    58  		applicationCartReceiverService *cartApplication.CartReceiverService
    59  
    60  		logger flamingo.Logger
    61  
    62  		formHandlerFactory application.FormHandlerFactory
    63  	}
    64  )
    65  
    66  // Inject - dependencies
    67  func (p *DefaultPersonalDataFormService) Inject(
    68  	applicationCartReceiverService *cartApplication.CartReceiverService,
    69  	defaultPersonalDataFormProvider DefaultPersonalDataFormProvider,
    70  	customerApplicationService *customerApplication.Service,
    71  	cfg *struct {
    72  		AdditionalFormValues    config.Slice `inject:"config:commerce.cart.personalDataForm.additionalFormFields,optional"`
    73  		DateOfBirthRequired     bool         `inject:"config:commerce.cart.personalDataForm.dateOfBirthRequired,optional"`
    74  		MinAge                  float64      `inject:"config:commerce.cart.personalDataForm.minAge,optional"`
    75  		PassportCountryRequired bool         `inject:"config:commerce.cart.personalDataForm.passportCountryRequired,optional"`
    76  		PassportNumberRequired  bool         `inject:"config:commerce.cart.personalDataForm.passportNumberRequired,optional"`
    77  	},
    78  ) *DefaultPersonalDataFormService {
    79  	p.applicationCartReceiverService = applicationCartReceiverService
    80  	p.defaultPersonalDataFormProvider = defaultPersonalDataFormProvider
    81  	p.customerApplicationService = customerApplicationService
    82  	if cfg != nil {
    83  		p.additionalFormFieldsCfg = cfg.AdditionalFormValues
    84  		p.dateOfBirthRequired = cfg.DateOfBirthRequired
    85  		p.minAge = int(cfg.MinAge)
    86  		p.passportCountryRequired = cfg.PassportCountryRequired
    87  		p.passportNumberRequired = cfg.PassportNumberRequired
    88  	}
    89  
    90  	return p
    91  }
    92  
    93  // GetFormData from data provider
    94  func (p *DefaultPersonalDataFormService) GetFormData(ctx context.Context, req *web.Request) (interface{}, error) {
    95  	formData := p.defaultPersonalDataFormProvider()
    96  
    97  	cart, err := p.applicationCartReceiverService.ViewCart(ctx, req.Session())
    98  	if err != nil {
    99  		return *formData, nil
   100  	}
   101  
   102  	customer, err := p.customerApplicationService.GetForIdentity(ctx, req)
   103  	if err == nil {
   104  		personalData := customer.GetPersonalData()
   105  		formData.DateOfBirth = personalData.Birthday.Format("2006-01-02")
   106  	}
   107  
   108  	if cart.Purchaser != nil {
   109  		formData.DateOfBirth = cart.Purchaser.PersonalDetails.DateOfBirth
   110  		formData.PassportCountry = cart.Purchaser.PersonalDetails.PassportCountry
   111  		formData.PassportNumber = cart.Purchaser.PersonalDetails.PassportNumber
   112  		if cart.Purchaser.Address != nil {
   113  			formData.Address.LoadFromCartAddress(*cart.Purchaser.Address)
   114  		}
   115  	}
   116  
   117  	if p.additionalFormFieldsCfg != nil {
   118  		for _, key := range p.additionalFormFieldsCfg {
   119  			formData.additionalFormData[key.(string)] = cart.AdditionalData.CustomAttributes[key.(string)]
   120  		}
   121  	}
   122  
   123  	return *formData, nil
   124  }
   125  
   126  // Decode fills the form data from the web request
   127  func (p *DefaultPersonalDataFormService) Decode(_ context.Context, _ *web.Request, values url.Values, _ interface{}) (interface{}, error) {
   128  	decoder := form.NewDecoder()
   129  	personalDataForm := p.defaultPersonalDataFormProvider()
   130  	err := decoder.Decode(personalDataForm, values)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	if p.additionalFormFieldsCfg != nil {
   136  		for _, key := range p.additionalFormFieldsCfg {
   137  			personalDataForm.additionalFormData[key.(string)] = values.Get(key.(string))
   138  		}
   139  	}
   140  
   141  	return personalDataForm, nil
   142  }
   143  
   144  // Validate form data
   145  func (p *DefaultPersonalDataFormService) Validate(ctx context.Context, req *web.Request, validatorProvider domain.ValidatorProvider, formData interface{}) (*domain.ValidationInfo, error) {
   146  	personalDataForm, ok := formData.(*DefaultPersonalDataForm)
   147  	if !ok {
   148  		return nil, errors.New("no DefaultPersonalDataForm given")
   149  	}
   150  	validationInfo := domain.ValidationInfo{}
   151  	validationInfo = validatorProvider.Validate(ctx, req, personalDataForm)
   152  
   153  	if p.dateOfBirthRequired && personalDataForm.DateOfBirth == "" {
   154  		validationInfo.AddFieldError("personalData.dateOfBirth", "formerror_dateOfBirth_required", "date of birth is required")
   155  	}
   156  
   157  	if personalDataForm.DateOfBirth != "" && !validateDOB(personalDataForm.DateOfBirth) {
   158  		validationInfo.AddFieldError("personalData.dateOfBirth", "formerror_dateOfBirth_formaterror", "date of birth has wrong format required: yyyy-mm-dd")
   159  	}
   160  
   161  	if p.minAge > 0 {
   162  		if !validateMinimumAge(personalDataForm.DateOfBirth, p.minAge) {
   163  			validationInfo.AddFieldError("personalData.dateOfBirth", "formerror_dateOfBirth_tooyoung", "you need to be at least "+strconv.Itoa(p.minAge))
   164  		}
   165  	}
   166  
   167  	if p.passportCountryRequired && personalDataForm.PassportCountry == "" {
   168  		validationInfo.AddFieldError("personalData.passportCountry", "formerror_passportCountry_required", "passport infos are required")
   169  	}
   170  
   171  	if p.passportNumberRequired && personalDataForm.PassportNumber == "" {
   172  		validationInfo.AddFieldError("personalData.passportNumber", "formerror_passportNumber_required", "passport infos are required")
   173  	}
   174  
   175  	return &validationInfo, nil
   176  }
   177  
   178  func validateDOB(value string) bool {
   179  	date, err := time.Parse("2006-01-02", value)
   180  	if err != nil {
   181  		return false
   182  	}
   183  
   184  	return date.Before(time.Now().Add(24 * time.Hour))
   185  }
   186  
   187  func validateMinimumAge(value string, minimumAge int) bool {
   188  	date, err := time.Parse("2006-01-02", value)
   189  	if err != nil {
   190  		return false
   191  	}
   192  
   193  	now := time.Now()
   194  	required := time.Date(
   195  		now.Year()-minimumAge,
   196  		now.Month(),
   197  		now.Day(),
   198  		0,
   199  		0,
   200  		0,
   201  		0,
   202  		date.Location(),
   203  	)
   204  
   205  	return date.Add(-time.Minute).Before(required)
   206  }
   207  
   208  // Inject dependencies
   209  func (c *PersonalDataFormController) Inject(
   210  	responder *web.Responder,
   211  	applicationCartService *cartApplication.CartService,
   212  	applicationCartReceiverService *cartApplication.CartReceiverService,
   213  	logger flamingo.Logger,
   214  	formHandlerFactory application.FormHandlerFactory,
   215  ) *PersonalDataFormController {
   216  	c.responder = responder
   217  	c.applicationCartReceiverService = applicationCartReceiverService
   218  	c.applicationCartService = applicationCartService
   219  
   220  	c.formHandlerFactory = formHandlerFactory
   221  	c.logger = logger
   222  
   223  	return c
   224  }
   225  
   226  // GetUnsubmittedForm returns a Unsubmitted form - using the registered FormService
   227  func (c *PersonalDataFormController) GetUnsubmittedForm(ctx context.Context, r *web.Request) (*domain.Form, error) {
   228  	formHandler, err := c.getFormHandler()
   229  	if err != nil {
   230  		return nil, err
   231  	}
   232  	return formHandler.HandleUnsubmittedForm(ctx, r)
   233  }
   234  
   235  // HandleFormAction handles post of personal data and updates cart
   236  func (c *PersonalDataFormController) HandleFormAction(ctx context.Context, r *web.Request) (form *domain.Form, actionSuccessFull bool, err error) {
   237  	session := web.SessionFromContext(ctx)
   238  	formHandler, err := c.getFormHandler()
   239  	if err != nil {
   240  		return nil, false, err
   241  	}
   242  	// ##  Handle the submitted form (validation etc)
   243  	form, err = formHandler.HandleSubmittedForm(ctx, r)
   244  	if err != nil {
   245  		return nil, false, err
   246  	}
   247  	personalDataForm, ok := form.Data.(PersonalDataForm)
   248  	if !ok {
   249  		return form, false, errors.New("cannot convert to PersonalDataForm ")
   250  	}
   251  	if !form.IsValidAndSubmitted() {
   252  		return form, false, nil
   253  	}
   254  
   255  	// UpdatePurchaser (and add additional data)
   256  	err = c.applicationCartService.UpdatePurchaser(ctx, session, personalDataForm.MapPerson(), personalDataForm.MapAdditionalData())
   257  	if err != nil {
   258  		c.logger.WithContext(ctx).Error("PersonalDataFormController UpdatePurchaser Error %v", err)
   259  		return form, false, err
   260  	}
   261  	return form, true, nil
   262  }
   263  
   264  func (c *PersonalDataFormController) getFormHandler() (domain.FormHandler, error) {
   265  	builder := c.formHandlerFactory.GetFormHandlerBuilder()
   266  	err := builder.SetNamedFormService("commerce.cart.personaldataFormService")
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  	return builder.Build(), nil
   271  }
   272  
   273  // Inject dependencies
   274  func (p *DefaultPersonalDataForm) Inject(
   275  	cfg *struct {
   276  		AdditionalFormValues config.Slice `inject:"config:commerce.cart.personalDataForm.additionalFormFields,optional"`
   277  	},
   278  ) *DefaultPersonalDataForm {
   279  	if cfg != nil {
   280  		p.additionalFormFieldsCfg = cfg.AdditionalFormValues
   281  	}
   282  
   283  	p.additionalFormData = make(map[string]string)
   284  
   285  	return p
   286  }
   287  
   288  // MapPerson maps the checkout form data to the cart.Person domain struct
   289  func (p *DefaultPersonalDataForm) MapPerson() *cart.Person {
   290  	person := cart.Person{
   291  		PersonalDetails: cart.PersonalDetails{
   292  			PassportNumber:  p.PassportNumber,
   293  			PassportCountry: p.PassportCountry,
   294  			DateOfBirth:     p.DateOfBirth,
   295  		},
   296  	}
   297  	return &person
   298  }
   299  
   300  // AdditionalData returns the additional form data
   301  func (p DefaultPersonalDataForm) AdditionalData(key string) string {
   302  	return p.additionalFormData[key]
   303  }
   304  
   305  // MapAdditionalData - mapper
   306  func (p DefaultPersonalDataForm) MapAdditionalData() *cart.AdditionalData {
   307  	return &cart.AdditionalData{
   308  		CustomAttributes: p.additionalFormData,
   309  	}
   310  }