refactoring
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing

This commit is contained in:
carla 2026-02-02 16:52:35 +01:00
parent aef3aa299f
commit 960506d16a
6 changed files with 57 additions and 31 deletions

View file

@ -191,8 +191,10 @@ defmodule Mv.Membership.Import.MemberCSV do
normalized != "" && not member_field?(normalized)
end)
|> Enum.map(fn header ->
"Unknown column '#{header}' will be ignored. " <>
"If this is a custom field, create it in Mila before importing."
gettext(
"Unknown column '%{header}' will be ignored. If this is a custom field, create it in Mila before importing.",
header: header
)
end)
{:ok, %{member: member_map, custom: custom_map}, warnings}
@ -311,7 +313,7 @@ defmodule Mv.Membership.Import.MemberCSV do
custom_field_lookup = Keyword.get(opts, :custom_field_lookup, %{})
existing_error_count = Keyword.get(opts, :existing_error_count, 0)
max_errors = Keyword.get(opts, :max_errors, @default_max_errors)
actor = Keyword.fetch!(opts, :actor)
actor = Keyword.get(opts, :actor, SystemActor.get_system_actor())
{inserted, failed, errors, _collected_error_count, truncated?} =
Enum.reduce(chunk_rows_with_lines, {0, 0, [], 0, false}, fn {line_number, row_map},
@ -607,13 +609,38 @@ defmodule Mv.Membership.Import.MemberCSV do
acc_values,
acc_errors
) do
# Trim value early and skip if empty
trimmed_value = if is_binary(value), do: String.trim(value), else: value
# Skip empty values (after trimming) - don't create CFV
if trimmed_value == "" or trimmed_value == nil do
{acc_values, acc_errors}
else
process_non_empty_custom_field(
custom_field_id_str,
trimmed_value,
custom_field_lookup,
acc_values,
acc_errors
)
end
end
# Processes a non-empty custom field value
defp process_non_empty_custom_field(
custom_field_id_str,
trimmed_value,
custom_field_lookup,
acc_values,
acc_errors
) do
case Map.get(custom_field_lookup, custom_field_id_str) do
nil ->
# Custom field not found, skip
{acc_values, acc_errors}
%{id: custom_field_id, value_type: value_type, name: custom_field_name} ->
case format_custom_field_value(value, value_type, custom_field_name) do
case format_custom_field_value(trimmed_value, value_type, custom_field_name) do
{:ok, formatted_value} ->
value_map = %{
"custom_field_id" => to_string(custom_field_id),
@ -691,17 +718,16 @@ defmodule Mv.Membership.Import.MemberCSV do
defp format_custom_field_value(value, :email, custom_field_name) when is_binary(value) do
trimmed = String.trim(value)
# Use simple validation: must contain @ and have valid format
# For CSV import, we use a simpler check than EctoCommons.EmailValidator
# to avoid dependencies and keep it fast
if String.contains?(trimmed, "@") and String.length(trimmed) >= 5 and
String.length(trimmed) <= 254 do
# Basic format check: username@domain.tld
if Regex.match?(~r/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/, trimmed) do
{:ok, %{"_union_type" => "email", "_union_value" => trimmed}}
else
{:error, format_custom_field_error(custom_field_name, :email, trimmed)}
end
# Use EctoCommons.EmailValidator for consistency with Member email validation
changeset =
{%{}, %{email: :string}}
|> Ecto.Changeset.cast(%{email: trimmed}, [:email])
|> EctoCommons.EmailValidator.validate_email(:email,
checks: Mv.Constants.email_validator_checks()
)
if changeset.valid? do
{:ok, %{"_union_type" => "email", "_union_value" => trimmed}}
else
{:error, format_custom_field_error(custom_field_name, :email, trimmed)}
end