From 3f44b963c896d1dc69709d62fbf9f7a26739891c Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 16:17:07 +0200 Subject: [PATCH 001/136] =?UTF-8?q?Fix=20the=20wording=20of=20the=20initia?= =?UTF-8?q?ted=20email=E2=80=99s=20subject?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/mails/initiated_mail.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/mails/initiated_mail.rb b/app/models/mails/initiated_mail.rb index 9d8edc903..7ffa4082e 100644 --- a/app/models/mails/initiated_mail.rb +++ b/app/models/mails/initiated_mail.rb @@ -5,7 +5,7 @@ module Mails SLUG = "initiated_mail" TEMPLATE_NAME = "mails/initiated_mail" DISPLAYED_NAME = 'Accusé de réception' - DEFAULT_OBJECT = 'Votre dossier TPS nº --numero_dossier-- a été bien reçu' + DEFAULT_OBJECT = 'Votre dossier TPS nº --numero_dossier-- a bien été reçu' ALLOWED_TAGS = [TAG_NUMERO_DOSSIER, TAG_LIEN_DOSSIER, TAG_LIBELLE_PROCEDURE] end end From 38e8908986b841de06528f193dd1905909b3e9e1 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 15:06:19 +0200 Subject: [PATCH 002/136] Fix the colors of the graphs on the Stats page --- app/views/stats/index.html.haml | 3 ++- config/initializers/chartkick.rb | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/stats/index.html.haml b/app/views/stats/index.html.haml index 4ca7c7914..a250973f3 100644 --- a/app/views/stats/index.html.haml +++ b/app/views/stats/index.html.haml @@ -49,7 +49,8 @@ .chart-container .chart - = pie_chart @procedures_count_per_administrateur + = pie_chart @procedures_count_per_administrateur, + colors: ["rgba(191, 220, 249, 1)", "rgba(113, 176, 239, 1)", "rgba(61, 149, 236, 1)"] - if administration_signed_in? .stat-card.stat-card-half.pull-left diff --git a/config/initializers/chartkick.rb b/config/initializers/chartkick.rb index 3fa55389c..c9269816f 100644 --- a/config/initializers/chartkick.rb +++ b/config/initializers/chartkick.rb @@ -1,4 +1,4 @@ Chartkick.options = { content_for: :charts_js, - colors: ["rgba(191, 220, 249, 1)", "rgba(113, 176, 239, 1)", "rgba(61, 149, 236, 1)"] + colors: ["rgba(61, 149, 236, 1)"] } From 4718d88f98980f8d23cea68987db05212740e249 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 11:26:04 +0200 Subject: [PATCH 003/136] Bump rubocop --- Gemfile.lock | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 07225ea01..50ff1bd59 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -417,6 +417,7 @@ GEM openstack (3.3.7) json orm_adapter (0.5.0) + parallel (1.11.2) parser (2.4.0.0) ast (~> 2.2) pg (0.19.0) @@ -471,7 +472,8 @@ GEM method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rainbow (2.2.1) + rainbow (2.2.2) + rake raindrops (0.17.0) rake (12.0.0) rb-fsevent (0.9.8) @@ -522,7 +524,8 @@ GEM rspec-mocks (~> 3.5.0) rspec-support (~> 3.5.0) rspec-support (3.5.0) - rubocop (0.48.1) + rubocop (0.49.1) + parallel (~> 1.10) parser (>= 2.3.3.1, < 3.0) powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) @@ -618,7 +621,7 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.7.2) - unicode-display_width (1.1.3) + unicode-display_width (1.2.1) unicode_utils (1.4.0) unicorn (5.2.0) kgio (~> 2.6) From 4e0d0100a229af4224f604c858d9994cb8e17b7e Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 11:49:08 +0200 Subject: [PATCH 004/136] rubocop.yml -> .rubocop.yml --- rubocop.yml => .rubocop.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename rubocop.yml => .rubocop.yml (100%) diff --git a/rubocop.yml b/.rubocop.yml similarity index 100% rename from rubocop.yml rename to .rubocop.yml From cf125e951458c99dc397bd3167f6e25c1ae62433 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 13:32:37 +0200 Subject: [PATCH 005/136] Add to the rubocop config a comprehensive list of cops --- .rubocop.yml | 1052 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1052 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index e69de29bb..4cc0f787b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -0,0 +1,1052 @@ +Bundler/DuplicatedGem: + Enabled: false + +Bundler/OrderedGems: + Enabled: false + +Layout/AccessModifierIndentation: + Enabled: false + +Layout/AlignArray: + Enabled: false + +Layout/AlignHash: + Enabled: false + +Layout/AlignParameters: + Enabled: false + +Layout/BlockEndNewline: + Enabled: false + +Layout/CaseIndentation: + Enabled: false + +Layout/ClosingParenthesisIndentation: + Enabled: false + +Layout/CommentIndentation: + Enabled: false + +Layout/DotPosition: + Enabled: false + +Layout/ElseAlignment: + Enabled: false + +Layout/EmptyLineAfterMagicComment: + Enabled: false + +Layout/EmptyLineBetweenDefs: + Enabled: false + +Layout/EmptyLines: + Enabled: false + +Layout/EmptyLinesAroundAccessModifier: + Enabled: false + +Layout/EmptyLinesAroundBeginBody: + Enabled: false + +Layout/EmptyLinesAroundBlockBody: + Enabled: false + +Layout/EmptyLinesAroundClassBody: + Enabled: false + +Layout/EmptyLinesAroundExceptionHandlingKeywords: + Enabled: false + +Layout/EmptyLinesAroundMethodBody: + Enabled: false + +Layout/EmptyLinesAroundModuleBody: + Enabled: false + +Layout/EndOfLine: + Enabled: false + +Layout/ExtraSpacing: + Enabled: false + +Layout/FirstArrayElementLineBreak: + Enabled: false + +Layout/FirstHashElementLineBreak: + Enabled: false + +Layout/FirstMethodArgumentLineBreak: + Enabled: false + +Layout/FirstMethodParameterLineBreak: + Enabled: false + +Layout/FirstParameterIndentation: + Enabled: false + +Layout/IndentArray: + Enabled: false + +Layout/IndentAssignment: + Enabled: false + +Layout/IndentHash: + Enabled: false + +Layout/IndentHeredoc: + Enabled: false + +Layout/IndentationConsistency: + Enabled: false + +Layout/IndentationWidth: + Enabled: false + +Layout/InitialIndentation: + Enabled: false + +Layout/LeadingCommentSpace: + Enabled: false + +Layout/MultilineArrayBraceLayout: + Enabled: false + +Layout/MultilineAssignmentLayout: + Enabled: false + +Layout/MultilineBlockLayout: + Enabled: false + +Layout/MultilineHashBraceLayout: + Enabled: false + +Layout/MultilineMethodCallBraceLayout: + Enabled: false + +Layout/MultilineMethodCallIndentation: + Enabled: false + +Layout/MultilineMethodDefinitionBraceLayout: + Enabled: false + +Layout/MultilineOperationIndentation: + Enabled: false + +Layout/RescueEnsureAlignment: + Enabled: false + +Layout/SpaceAfterColon: + Enabled: false + +Layout/SpaceAfterComma: + Enabled: false + +Layout/SpaceAfterMethodName: + Enabled: false + +Layout/SpaceAfterNot: + Enabled: false + +Layout/SpaceAfterSemicolon: + Enabled: false + +Layout/SpaceAroundBlockParameters: + Enabled: false + +Layout/SpaceAroundEqualsInParameterDefault: + Enabled: false + +Layout/SpaceAroundKeyword: + Enabled: false + +Layout/SpaceAroundOperators: + Enabled: false + +Layout/SpaceBeforeBlockBraces: + Enabled: false + +Layout/SpaceBeforeComma: + Enabled: false + +Layout/SpaceBeforeComment: + Enabled: false + +Layout/SpaceBeforeFirstArg: + Enabled: false + +Layout/SpaceBeforeSemicolon: + Enabled: false + +Layout/SpaceInLambdaLiteral: + Enabled: false + +Layout/SpaceInsideArrayPercentLiteral: + Enabled: false + +Layout/SpaceInsideBlockBraces: + Enabled: false + +Layout/SpaceInsideBrackets: + Enabled: false + +Layout/SpaceInsideHashLiteralBraces: + Enabled: false + +Layout/SpaceInsideParens: + Enabled: false + +Layout/SpaceInsidePercentLiteralDelimiters: + Enabled: false + +Layout/SpaceInsideRangeLiteral: + Enabled: false + +Layout/SpaceInsideStringInterpolation: + Enabled: false + +Layout/Tab: + Enabled: false + +Layout/TrailingBlankLines: + Enabled: false + +Layout/TrailingWhitespace: + Enabled: false + +Lint/AmbiguousBlockAssociation: + Enabled: false + +Lint/AmbiguousOperator: + Enabled: false + +Lint/AmbiguousRegexpLiteral: + Enabled: false + +Lint/AssignmentInCondition: + Enabled: false + +Lint/BlockAlignment: + Enabled: false + +Lint/CircularArgumentReference: + Enabled: false + +Lint/ConditionPosition: + Enabled: false + +Lint/Debugger: + Enabled: false + +Lint/DefEndAlignment: + Enabled: false + +Lint/DeprecatedClassMethods: + Enabled: false + +Lint/DuplicateCaseCondition: + Enabled: false + +Lint/DuplicateMethods: + Enabled: false + +Lint/DuplicatedKey: + Enabled: false + +Lint/EachWithObjectArgument: + Enabled: false + +Lint/ElseLayout: + Enabled: false + +Lint/EmptyEnsure: + Enabled: false + +Lint/EmptyExpression: + Enabled: false + +Lint/EmptyInterpolation: + Enabled: false + +Lint/EmptyWhen: + Enabled: false + +Lint/EndAlignment: + Enabled: false + +Lint/EndInMethod: + Enabled: false + +Lint/EnsureReturn: + Enabled: false + +Lint/FloatOutOfRange: + Enabled: false + +Lint/FormatParameterMismatch: + Enabled: false + +Lint/HandleExceptions: + Enabled: false + +Lint/ImplicitStringConcatenation: + Enabled: false + +Lint/IneffectiveAccessModifier: + Enabled: false + +Lint/InheritException: + Enabled: false + +Lint/InvalidCharacterLiteral: + Enabled: false + +Lint/LiteralInCondition: + Enabled: false + +Lint/LiteralInInterpolation: + Enabled: false + +Lint/Loop: + Enabled: false + +Lint/MultipleCompare: + Enabled: false + +Lint/NestedMethodDefinition: + Enabled: false + +Lint/NextWithoutAccumulator: + Enabled: false + +Lint/NonLocalExitFromIterator: + Enabled: false + +Lint/ParenthesesAsGroupedExpression: + Enabled: false + +Lint/PercentStringArray: + Enabled: false + +Lint/PercentSymbolArray: + Enabled: false + +Lint/RandOne: + Enabled: false + +Lint/RequireParentheses: + Enabled: false + +Lint/RescueException: + Enabled: false + +Lint/RescueType: + Enabled: false + +Lint/ReturnInVoidContext: + Enabled: false + +Lint/SafeNavigationChain: + Enabled: false + +Lint/ScriptPermission: + Enabled: false + +Lint/ShadowedException: + Enabled: false + +Lint/ShadowingOuterLocalVariable: + Enabled: false + +Lint/StringConversionInInterpolation: + Enabled: false + +Lint/Syntax: + Enabled: false + +Lint/UnderscorePrefixedVariableName: + Enabled: false + +Lint/UnifiedInteger: + Enabled: false + +Lint/UnneededDisable: + Enabled: false + +Lint/UnneededSplatExpansion: + Enabled: false + +Lint/UnreachableCode: + Enabled: false + +Lint/UnusedBlockArgument: + Enabled: false + +Lint/UnusedMethodArgument: + Enabled: false + +Lint/UselessAccessModifier: + Enabled: false + +Lint/UselessAssignment: + Enabled: false + +Lint/UselessComparison: + Enabled: false + +Lint/UselessElseWithoutRescue: + Enabled: false + +Lint/UselessSetterCall: + Enabled: false + +Lint/Void: + Enabled: false + +Metrics/AbcSize: + Enabled: false + +Metrics/BlockLength: + Enabled: false + +Metrics/BlockNesting: + Enabled: false + +Metrics/ClassLength: + Enabled: false + +Metrics/CyclomaticComplexity: + Enabled: false + +Metrics/LineLength: + Enabled: false + +Metrics/MethodLength: + Enabled: false + +Metrics/ModuleLength: + Enabled: false + +Metrics/ParameterLists: + Enabled: false + +Metrics/PerceivedComplexity: + Enabled: false + +Performance/Caller: + Enabled: false + +Performance/CaseWhenSplat: + Enabled: false + +Performance/Casecmp: + Enabled: false + +Performance/CompareWithBlock: + Enabled: false + +Performance/Count: + Enabled: false + +Performance/Detect: + Enabled: false + +Performance/DoubleStartEndWith: + Enabled: false + +Performance/EndWith: + Enabled: false + +Performance/FixedSize: + Enabled: false + +Performance/FlatMap: + Enabled: false + +Performance/HashEachMethods: + Enabled: false + +Performance/LstripRstrip: + Enabled: false + +Performance/RangeInclude: + Enabled: false + +Performance/RedundantBlockCall: + Enabled: false + +Performance/RedundantMatch: + Enabled: false + +Performance/RedundantMerge: + Enabled: false + +Performance/RedundantSortBy: + Enabled: false + +Performance/RegexpMatch: + Enabled: false + +Performance/ReverseEach: + Enabled: false + +Performance/Sample: + Enabled: false + +Performance/Size: + Enabled: false + +Performance/StartWith: + Enabled: false + +Performance/StringReplacement: + Enabled: false + +Performance/TimesMap: + Enabled: false + +Rails/ActionFilter: + Enabled: false + +Rails/ActiveSupportAliases: + Enabled: false + +Rails/ApplicationJob: + Enabled: false + +Rails/ApplicationRecord: + Enabled: false + +Rails/Blank: + Enabled: false + +Rails/Date: + Enabled: false + +Rails/Delegate: + Enabled: false + +Rails/DelegateAllowBlank: + Enabled: false + +Rails/DynamicFindBy: + Enabled: false + +Rails/EnumUniqueness: + Enabled: false + +Rails/Exit: + Enabled: false + +Rails/FilePath: + Enabled: false + +Rails/FindBy: + Enabled: false + +Rails/FindEach: + Enabled: false + +Rails/HasAndBelongsToMany: + Enabled: false + +Rails/HttpPositionalArguments: + Enabled: false + +Rails/NotNullColumn: + Enabled: false + +Rails/Output: + Enabled: false + +Rails/OutputSafety: + Enabled: false + +Rails/PluralizationGrammar: + Enabled: false + +Rails/Present: + Enabled: false + +Rails/ReadWriteAttribute: + Enabled: false + +Rails/RelativeDateConstant: + Enabled: false + +Rails/RequestReferer: + Enabled: false + +Rails/ReversibleMigration: + Enabled: false + +Rails/SafeNavigation: + Enabled: false + +Rails/SaveBang: + Enabled: false + +Rails/ScopeArgs: + Enabled: false + +Rails/SkipsModelValidations: + Enabled: false + +Rails/TimeZone: + Enabled: false + +Rails/UniqBeforePluck: + Enabled: false + +Rails/Validation: + Enabled: false + +Rails: + Enabled: false + +Security/Eval: + Enabled: false + +Security/JSONLoad: + Enabled: false + +Security/MarshalLoad: + Enabled: false + +Security/YAMLLoad: + Enabled: false + +Style/AccessorMethodName: + Enabled: false + +Style/Alias: + Enabled: false + +Style/AndOr: + Enabled: false + +Style/ArrayJoin: + Enabled: false + +Style/AsciiComments: + Enabled: false + +Style/AsciiIdentifiers: + Enabled: false + +Style/Attr: + Enabled: false + +Style/AutoResourceCleanup: + Enabled: false + +Style/BarePercentLiterals: + Enabled: false + +Style/BeginBlock: + Enabled: false + +Style/BlockComments: + Enabled: false + +Style/BlockDelimiters: + Enabled: false + +Style/BracesAroundHashParameters: + Enabled: false + +Style/CaseEquality: + Enabled: false + +Style/CharacterLiteral: + Enabled: false + +Style/ClassAndModuleCamelCase: + Enabled: false + +Style/ClassAndModuleChildren: + Enabled: false + +Style/ClassCheck: + Enabled: false + +Style/ClassMethods: + Enabled: false + +Style/ClassVars: + Enabled: false + +Style/CollectionMethods: + Enabled: false + +Style/ColonMethodCall: + Enabled: false + +Style/CommandLiteral: + Enabled: false + +Style/CommentAnnotation: + Enabled: false + +Style/ConditionalAssignment: + Enabled: false + +Style/ConstantName: + Enabled: false + +Style/Copyright: + Enabled: false + +Style/DefWithParentheses: + Enabled: false + +Style/Documentation: + Enabled: false + +Style/DocumentationMethod: + Enabled: false + +Style/DoubleNegation: + Enabled: false + +Style/EachForSimpleLoop: + Enabled: false + +Style/EachWithObject: + Enabled: false + +Style/EmptyCaseCondition: + Enabled: false + +Style/EmptyElse: + Enabled: false + +Style/EmptyLiteral: + Enabled: false + +Style/EmptyMethod: + Enabled: false + +Style/Encoding: + Enabled: false + +Style/EndBlock: + Enabled: false + +Style/EvenOdd: + Enabled: false + +Style/FileName: + Enabled: false + +Style/FlipFlop: + Enabled: false + +Style/For: + Enabled: false + +Style/FormatString: + Enabled: false + +Style/FormatStringToken: + Enabled: false + +Style/FrozenStringLiteralComment: + Enabled: false + +Style/GlobalVars: + Enabled: false + +Style/GuardClause: + Enabled: false + +Style/HashSyntax: + Enabled: false + +Style/HeredocDelimiters: + Enabled: false + +Style/IdenticalConditionalBranches: + Enabled: false + +Style/IfInsideElse: + Enabled: false + +Style/IfUnlessModifier: + Enabled: false + +Style/IfUnlessModifierOfIfUnless: + Enabled: false + +Style/IfWithSemicolon: + Enabled: false + +Style/ImplicitRuntimeError: + Enabled: false + +Style/InfiniteLoop: + Enabled: false + +Style/InlineComment: + Enabled: false + +Style/InverseMethods: + Enabled: false + +Style/Lambda: + Enabled: false + +Style/LambdaCall: + Enabled: false + +Style/LineEndConcatenation: + Enabled: false + +Style/MethodCallWithArgsParentheses: + Enabled: false + +Style/MethodCallWithoutArgsParentheses: + Enabled: false + +Style/MethodCalledOnDoEndBlock: + Enabled: false + +Style/MethodDefParentheses: + Enabled: false + +Style/MethodMissing: + Enabled: false + +Style/MethodName: + Enabled: false + +Style/MissingElse: + Enabled: false + +Style/MixinGrouping: + Enabled: false + +Style/ModuleFunction: + Enabled: false + +Style/MultilineBlockChain: + Enabled: false + +Style/MultilineIfModifier: + Enabled: false + +Style/MultilineIfThen: + Enabled: false + +Style/MultilineMemoization: + Enabled: false + +Style/MultilineTernaryOperator: + Enabled: false + +Style/MultipleComparison: + Enabled: false + +Style/MutableConstant: + Enabled: false + +Style/NegatedIf: + Enabled: false + +Style/NegatedWhile: + Enabled: false + +Style/NestedModifier: + Enabled: false + +Style/NestedParenthesizedCalls: + Enabled: false + +Style/NestedTernaryOperator: + Enabled: false + +Style/Next: + Enabled: false + +Style/NilComparison: + Enabled: false + +Style/NonNilCheck: + Enabled: false + +Style/Not: + Enabled: false + +Style/NumericLiteralPrefix: + Enabled: false + +Style/NumericLiterals: + Enabled: false + +Style/NumericPredicate: + Enabled: false + +Style/OneLineConditional: + Enabled: false + +Style/OpMethod: + Enabled: false + +Style/OptionHash: + Enabled: false + +Style/OptionalArguments: + Enabled: false + +Style/ParallelAssignment: + Enabled: false + +Style/ParenthesesAroundCondition: + Enabled: false + +Style/PercentLiteralDelimiters: + Enabled: false + +Style/PercentQLiterals: + Enabled: false + +Style/PerlBackrefs: + Enabled: false + +Style/PredicateName: + Enabled: false + +Style/PreferredHashMethods: + Enabled: false + +Style/Proc: + Enabled: false + +Style/RaiseArgs: + Enabled: false + +Style/RedundantBegin: + Enabled: false + +Style/RedundantException: + Enabled: false + +Style/RedundantFreeze: + Enabled: false + +Style/RedundantParentheses: + Enabled: false + +Style/RedundantReturn: + Enabled: false + +Style/RedundantSelf: + Enabled: false + +Style/RegexpLiteral: + Enabled: false + +Style/RescueModifier: + Enabled: false + +Style/SafeNavigation: + Enabled: false + +Style/SelfAssignment: + Enabled: false + +Style/Semicolon: + Enabled: false + +Style/Send: + Enabled: false + +Style/SignalException: + Enabled: false + +Style/SingleLineBlockParams: + Enabled: false + +Style/SingleLineMethods: + Enabled: false + +Style/SpecialGlobalVars: + Enabled: false + +Style/StabbyLambdaParentheses: + Enabled: false + +Style/StringLiterals: + Enabled: false + +Style/StringLiteralsInInterpolation: + Enabled: false + +Style/StringMethods: + Enabled: false + +Style/StructInheritance: + Enabled: false + +Style/SymbolArray: + Enabled: false + +Style/SymbolLiteral: + Enabled: false + +Style/SymbolProc: + Enabled: false + +Style/TernaryParentheses: + Enabled: false + +Style/TrailingCommaInArguments: + Enabled: false + +Style/TrailingCommaInLiteral: + Enabled: false + +Style/TrailingUnderscoreVariable: + Enabled: false + +Style/TrivialAccessors: + Enabled: false + +Style/UnlessElse: + Enabled: false + +Style/UnneededCapitalW: + Enabled: false + +Style/UnneededInterpolation: + Enabled: false + +Style/UnneededPercentQ: + Enabled: false + +Style/VariableInterpolation: + Enabled: false + +Style/VariableName: + Enabled: false + +Style/VariableNumber: + Enabled: false + +Style/WhenThen: + Enabled: false + +Style/WhileUntilDo: + Enabled: false + +Style/WhileUntilModifier: + Enabled: false + +Style/WordArray: + Enabled: false + +Style/YodaCondition: + Enabled: false + +Style/ZeroLengthPredicate: + Enabled: false From 75e995f721ff63c24dda77a934d3aa544e8d9e0e Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 13:34:04 +0200 Subject: [PATCH 006/136] Enable several rubocop cops --- .rubocop.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 4cc0f787b..be5922908 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,11 +1,11 @@ Bundler/DuplicatedGem: - Enabled: false + Enabled: true Bundler/OrderedGems: Enabled: false Layout/AccessModifierIndentation: - Enabled: false + Enabled: true Layout/AlignArray: Enabled: false @@ -23,7 +23,7 @@ Layout/CaseIndentation: Enabled: false Layout/ClosingParenthesisIndentation: - Enabled: false + Enabled: true Layout/CommentIndentation: Enabled: false @@ -32,10 +32,10 @@ Layout/DotPosition: Enabled: false Layout/ElseAlignment: - Enabled: false + Enabled: true Layout/EmptyLineAfterMagicComment: - Enabled: false + Enabled: true Layout/EmptyLineBetweenDefs: Enabled: false @@ -47,7 +47,7 @@ Layout/EmptyLinesAroundAccessModifier: Enabled: false Layout/EmptyLinesAroundBeginBody: - Enabled: false + Enabled: true Layout/EmptyLinesAroundBlockBody: Enabled: false @@ -65,7 +65,8 @@ Layout/EmptyLinesAroundModuleBody: Enabled: false Layout/EndOfLine: - Enabled: false + Enabled: true + EnforcedStyle: lf Layout/ExtraSpacing: Enabled: false @@ -272,7 +273,8 @@ Lint/EmptyWhen: Enabled: false Lint/EndAlignment: - Enabled: false + Enabled: true + EnforcedStyleAlignWith: variable Lint/EndInMethod: Enabled: false From 50fad54538c96331c5c8a5a53ec0e27140caedf3 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 13:34:19 +0200 Subject: [PATCH 007/136] Enable the Layout/AlignArray cop --- .rubocop.yml | 2 +- app/models/procedure.rb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index be5922908..e9db9c4fc 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,7 +8,7 @@ Layout/AccessModifierIndentation: Enabled: true Layout/AlignArray: - Enabled: false + Enabled: true Layout/AlignHash: Enabled: false diff --git a/app/models/procedure.rb b/app/models/procedure.rb index c46b2d6da..99d44c77e 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -88,7 +88,8 @@ class Procedure < ActiveRecord::Base def clone procedure = self.deep_clone(include: - [:types_de_piece_justificative, + [ + :types_de_piece_justificative, :types_de_champ, :types_de_champ_private, :module_api_carto, From 9439e1abb131b451e92c812076b4728d3f1ff5ee Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 13:49:51 +0200 Subject: [PATCH 008/136] Enable the Layout/AlignParameters cop --- .rubocop.yml | 3 +- .../admin/accompagnateurs_controller.rb | 12 ++--- .../admin/gestionnaires_controller.rb | 7 +-- .../admin/procedures_controller.rb | 18 +++---- app/controllers/administrations_controller.rb | 8 ++- .../backoffice/dossiers_controller.rb | 16 +++--- .../backoffice/dossiers_list_controller.rb | 8 +-- app/controllers/backoffice_controller.rb | 12 ++--- app/controllers/commentaires_controller.rb | 6 +-- app/controllers/demo_controller.rb | 6 +-- app/controllers/users/dossiers_controller.rb | 6 +-- app/controllers/users_controller.rb | 4 +- app/lib/carto/sgmap/api.rb | 4 +- app/models/administrateur.rb | 2 +- app/models/gestionnaire.rb | 2 +- app/models/piece_justificative.rb | 2 +- app/models/user.rb | 2 +- app/serializers/cerfa_serializer.rb | 2 +- app/serializers/commentaire_serializer.rb | 4 +- app/serializers/dossier_serializer.rb | 22 ++++---- .../dossier_table_export_serializer.rb | 22 ++++---- app/serializers/dossiers_serializer.rb | 2 +- app/serializers/entreprise_serializer.rb | 22 ++++---- app/serializers/etablissement_serializer.rb | 23 ++++----- .../module_api_carto_serializer.rb | 6 +-- .../piece_justificative_serializer.rb | 4 +- app/serializers/procedure_serializer.rb | 13 +++-- app/serializers/type_de_champ_serializer.rb | 8 +-- .../type_de_piece_justificative_serializer.rb | 8 +-- ...preference_list_dossier_controller_spec.rb | 13 +++-- .../private_formulaires_controller_spec.rb | 11 ++-- spec/facades/dossiers_list_facades_spec.rb | 42 ++++++++-------- .../france_connect_particulier_spec.rb | 14 +++--- ...dossiers_list_gestionnaire_service_spec.rb | 12 ++--- .../accompagnateurs/show.html.haml_spec.rb | 24 ++++----- .../gestionnaires/index.html.haml_spec.rb | 12 ++--- .../previsualisations/show.html.haml_spec.rb | 3 +- .../dossiers/index_html.haml_spec.rb | 50 +++++++++---------- .../users/dossiers/index_html.haml_spec.rb | 6 +-- 39 files changed, 221 insertions(+), 220 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index e9db9c4fc..9a19249af 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -14,7 +14,8 @@ Layout/AlignHash: Enabled: false Layout/AlignParameters: - Enabled: false + Enabled: true + EnforcedStyle: with_fixed_indentation Layout/BlockEndNewline: Enabled: false diff --git a/app/controllers/admin/accompagnateurs_controller.rb b/app/controllers/admin/accompagnateurs_controller.rb index dcdb3ae46..6f6314851 100644 --- a/app/controllers/admin/accompagnateurs_controller.rb +++ b/app/controllers/admin/accompagnateurs_controller.rb @@ -7,17 +7,17 @@ class Admin::AccompagnateursController < AdminController def show assign_scope = @procedure.gestionnaires @accompagnateurs_assign = smart_listing_create :accompagnateurs_assign, - assign_scope, - partial: "admin/accompagnateurs/list_assign", - array: true + assign_scope, + partial: "admin/accompagnateurs/list_assign", + array: true not_assign_scope = current_administrateur.gestionnaires.where.not(id: assign_scope.ids) not_assign_scope = not_assign_scope.where("email LIKE '%#{params[:filter]}%'") if params[:filter] @accompagnateurs_not_assign = smart_listing_create :accompagnateurs_not_assign, - not_assign_scope, - partial: "admin/accompagnateurs/list_not_assign", - array: true + not_assign_scope, + partial: "admin/accompagnateurs/list_not_assign", + array: true @gestionnaire ||= Gestionnaire.new end diff --git a/app/controllers/admin/gestionnaires_controller.rb b/app/controllers/admin/gestionnaires_controller.rb index da082c5f3..e2e5aa343 100644 --- a/app/controllers/admin/gestionnaires_controller.rb +++ b/app/controllers/admin/gestionnaires_controller.rb @@ -4,9 +4,10 @@ class Admin::GestionnairesController < AdminController def index @gestionnaires = smart_listing_create :gestionnaires, - current_administrateur.gestionnaires, - partial: "admin/gestionnaires/list", - array: true + current_administrateur.gestionnaires, + partial: "admin/gestionnaires/list", + array: true + @gestionnaire ||= Gestionnaire.new end diff --git a/app/controllers/admin/procedures_controller.rb b/app/controllers/admin/procedures_controller.rb index 639c79242..3ca4c0fb9 100644 --- a/app/controllers/admin/procedures_controller.rb +++ b/app/controllers/admin/procedures_controller.rb @@ -6,18 +6,18 @@ class Admin::ProceduresController < AdminController def index @procedures = smart_listing_create :procedures, - current_administrateur.procedures.where(published: true, archived: false).order(created_at: :desc), - partial: "admin/procedures/list", - array: true + current_administrateur.procedures.where(published: true, archived: false).order(created_at: :desc), + partial: "admin/procedures/list", + array: true active_class end def archived @procedures = smart_listing_create :procedures, - current_administrateur.procedures.where(archived: true).order(created_at: :desc), - partial: "admin/procedures/list", - array: true + current_administrateur.procedures.where(archived: true).order(created_at: :desc), + partial: "admin/procedures/list", + array: true archived_class @@ -26,9 +26,9 @@ class Admin::ProceduresController < AdminController def draft @procedures = smart_listing_create :procedures, - current_administrateur.procedures.where(published: false, archived: false).order(created_at: :desc), - partial: "admin/procedures/list", - array: true + current_administrateur.procedures.where(published: false, archived: false).order(created_at: :desc), + partial: "admin/procedures/list", + array: true draft_class diff --git a/app/controllers/administrations_controller.rb b/app/controllers/administrations_controller.rb index e610ffcf5..26ebc9087 100644 --- a/app/controllers/administrations_controller.rb +++ b/app/controllers/administrations_controller.rb @@ -8,11 +8,9 @@ class AdministrationsController < ApplicationController @admin = Administrateur.new @admins = smart_listing_create :admins, - Administrateur.all.order(:email), - partial: "administrations/list", - array: true - - + Administrateur.all.order(:email), + partial: "administrations/list", + array: true end def create diff --git a/app/controllers/backoffice/dossiers_controller.rb b/app/controllers/backoffice/dossiers_controller.rb index 753b40302..7014e8c8d 100644 --- a/app/controllers/backoffice/dossiers_controller.rb +++ b/app/controllers/backoffice/dossiers_controller.rb @@ -79,17 +79,17 @@ class Backoffice::DossiersController < Backoffice::DossiersListController end smart_listing_create :search, - @dossiers, - partial: "backoffice/dossiers/list", - array: true, - default_sort: dossiers_list_facade.service.default_sort + @dossiers, + partial: "backoffice/dossiers/list", + array: true, + default_sort: dossiers_list_facade.service.default_sort rescue RuntimeError smart_listing_create :search, - [], - partial: "backoffice/dossiers/list", - array: true, - default_sort: dossiers_list_facade.service.default_sort + [], + partial: "backoffice/dossiers/list", + array: true, + default_sort: dossiers_list_facade.service.default_sort end def receive diff --git a/app/controllers/backoffice/dossiers_list_controller.rb b/app/controllers/backoffice/dossiers_list_controller.rb index 52274bb79..a61c0caf6 100644 --- a/app/controllers/backoffice/dossiers_list_controller.rb +++ b/app/controllers/backoffice/dossiers_list_controller.rb @@ -48,10 +48,10 @@ class Backoffice::DossiersListController < ApplicationController def default_smart_listing_create name, collection smart_listing_create name, - collection, - partial: 'backoffice/dossiers/list', - array: true, - default_sort: dossiers_list_facade.service.default_sort + collection, + partial: 'backoffice/dossiers/list', + array: true, + default_sort: dossiers_list_facade.service.default_sort end def param_smart_listing diff --git a/app/controllers/backoffice_controller.rb b/app/controllers/backoffice_controller.rb index 5f426be36..3188124c6 100644 --- a/app/controllers/backoffice_controller.rb +++ b/app/controllers/backoffice_controller.rb @@ -15,14 +15,14 @@ class BackofficeController < ApplicationController def invitations pending_avis = current_gestionnaire.avis.without_answer.includes(dossier: [:procedure]).by_latest @pending_avis = smart_listing_create :pending_avis, - pending_avis, - partial: 'backoffice/dossiers/list_invitations', - array: true + pending_avis, + partial: 'backoffice/dossiers/list_invitations', + array: true avis_with_answer = current_gestionnaire.avis.with_answer.includes(dossier: [:procedure]).by_latest @avis_with_answer = smart_listing_create :avis_with_answer, - avis_with_answer, - partial: 'backoffice/dossiers/list_invitations', - array: true + avis_with_answer, + partial: 'backoffice/dossiers/list_invitations', + array: true end end diff --git a/app/controllers/commentaires_controller.rb b/app/controllers/commentaires_controller.rb index b22ada7eb..7a4dcc8db 100644 --- a/app/controllers/commentaires_controller.rb +++ b/app/controllers/commentaires_controller.rb @@ -1,9 +1,9 @@ class CommentairesController < ApplicationController def index @facade = DossierFacades.new( - params[:dossier_id], - (current_gestionnaire || current_user).email, - params[:champs_id] + params[:dossier_id], + (current_gestionnaire || current_user).email, + params[:champs_id] ) render layout: false rescue ActiveRecord::RecordNotFound diff --git a/app/controllers/demo_controller.rb b/app/controllers/demo_controller.rb index 5933c5735..54f45464d 100644 --- a/app/controllers/demo_controller.rb +++ b/app/controllers/demo_controller.rb @@ -6,8 +6,8 @@ class DemoController < ApplicationController return redirect_to root_path if Rails.env.production? smart_listing_create :procedures, - Procedure.where(archived: false, published: true).order("id DESC"), - partial: "demo/list", - array: true + Procedure.where(archived: false, published: true).order("id DESC"), + partial: "demo/list", + array: true end end diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 1d9964d72..8decc82ca 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -30,9 +30,9 @@ class Users::DossiersController < UsersController end @dossiers = smart_listing_create :dossiers, - @dossiers_filtered, - partial: "users/dossiers/list", - array: true + @dossiers_filtered, + partial: "users/dossiers/list", + array: true end def commencer diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 67c94e85f..3f013040e 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -17,8 +17,8 @@ class UsersController < ApplicationController def authorized_routes? controller redirect_to_root_path 'Le status de votre dossier n\'autorise pas cette URL' unless UserRoutesAuthorizationService.authorized_route?( - controller, - current_user_dossier) + controller, + current_user_dossier) rescue ActiveRecord::RecordNotFound redirect_to_root_path 'Vous n’avez pas accès à ce dossier.' end diff --git a/app/lib/carto/sgmap/api.rb b/app/lib/carto/sgmap/api.rb index db29d7d0b..ef72fc062 100644 --- a/app/lib/carto/sgmap/api.rb +++ b/app/lib/carto/sgmap/api.rb @@ -18,8 +18,8 @@ class CARTO::SGMAP::API verify_ssl_mode = OpenSSL::SSL::VERIFY_NONE RestClient::Resource.new( - url, - verify_ssl: verify_ssl_mode, + url, + verify_ssl: verify_ssl_mode, ).post params[:geojson], content_type: 'application/json' rescue RestClient::InternalServerError diff --git a/app/models/administrateur.rb b/app/models/administrateur.rb index 731e5df5c..072fd8c9d 100644 --- a/app/models/administrateur.rb +++ b/app/models/administrateur.rb @@ -1,6 +1,6 @@ class Administrateur < ActiveRecord::Base devise :database_authenticatable, :registerable, - :recoverable, :rememberable, :trackable, :validatable + :recoverable, :rememberable, :trackable, :validatable has_and_belongs_to_many :gestionnaires has_many :procedures diff --git a/app/models/gestionnaire.rb b/app/models/gestionnaire.rb index 6492cca64..190b9502c 100644 --- a/app/models/gestionnaire.rb +++ b/app/models/gestionnaire.rb @@ -1,6 +1,6 @@ class Gestionnaire < ActiveRecord::Base devise :database_authenticatable, :registerable, - :recoverable, :rememberable, :trackable, :validatable + :recoverable, :rememberable, :trackable, :validatable has_and_belongs_to_many :administrateurs diff --git a/app/models/piece_justificative.rb b/app/models/piece_justificative.rb index c9537adf0..6b08703b4 100644 --- a/app/models/piece_justificative.rb +++ b/app/models/piece_justificative.rb @@ -33,7 +33,7 @@ class PieceJustificative < ActiveRecord::Base (RemoteDownloader.new content.filename).url else (LocalDownloader.new content.path, - (type_de_piece_justificative.nil? ? content.original_filename : type_de_piece_justificative.libelle)).url + (type_de_piece_justificative.nil? ? content.original_filename : type_de_piece_justificative.libelle)).url end end end diff --git a/app/models/user.rb b/app/models/user.rb index c4dab2a5f..3189c652d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -7,7 +7,7 @@ class User < ActiveRecord::Base # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, - :recoverable, :rememberable, :trackable, :validatable + :recoverable, :rememberable, :trackable, :validatable has_many :dossiers, dependent: :destroy has_many :invites, dependent: :destroy diff --git a/app/serializers/cerfa_serializer.rb b/app/serializers/cerfa_serializer.rb index a5b832936..1971db96a 100644 --- a/app/serializers/cerfa_serializer.rb +++ b/app/serializers/cerfa_serializer.rb @@ -1,6 +1,6 @@ class CerfaSerializer < ActiveModel::Serializer attributes :created_at, - :content_url + :content_url has_one :user end diff --git a/app/serializers/commentaire_serializer.rb b/app/serializers/commentaire_serializer.rb index 587cde833..c7366b65b 100644 --- a/app/serializers/commentaire_serializer.rb +++ b/app/serializers/commentaire_serializer.rb @@ -1,5 +1,5 @@ class CommentaireSerializer < ActiveModel::Serializer attributes :email, - :body, - :created_at + :body, + :created_at end diff --git a/app/serializers/dossier_serializer.rb b/app/serializers/dossier_serializer.rb index 73b7dcc8c..c731d452b 100644 --- a/app/serializers/dossier_serializer.rb +++ b/app/serializers/dossier_serializer.rb @@ -1,16 +1,16 @@ class DossierSerializer < ActiveModel::Serializer attributes :id, - :created_at, - :updated_at, - :archived, - :mandataire_social, - :state, - :simplified_state, - :initiated_at, - :received_at, - :processed_at, - :accompagnateurs, - :invites + :created_at, + :updated_at, + :archived, + :mandataire_social, + :state, + :simplified_state, + :initiated_at, + :received_at, + :processed_at, + :accompagnateurs, + :invites has_one :entreprise has_one :etablissement diff --git a/app/serializers/dossier_table_export_serializer.rb b/app/serializers/dossier_table_export_serializer.rb index 3bd4ba52a..84e569787 100644 --- a/app/serializers/dossier_table_export_serializer.rb +++ b/app/serializers/dossier_table_export_serializer.rb @@ -1,20 +1,20 @@ class DossierTableExportSerializer < ActiveModel::Serializer attributes :id, - :created_at, - :updated_at, - :archived, - :mandataire_social, - :state, - :initiated_at, - :received_at, - :processed_at + :created_at, + :updated_at, + :archived, + :mandataire_social, + :state, + :initiated_at, + :received_at, + :processed_at attribute :emails_accompagnateurs attributes :individual_gender, - :individual_prenom, - :individual_nom, - :individual_birthdate + :individual_prenom, + :individual_nom, + :individual_birthdate def individual_prenom object.individual.try(:prenom) diff --git a/app/serializers/dossiers_serializer.rb b/app/serializers/dossiers_serializer.rb index e2b53b679..e43f4b63d 100644 --- a/app/serializers/dossiers_serializer.rb +++ b/app/serializers/dossiers_serializer.rb @@ -1,4 +1,4 @@ class DossiersSerializer < ActiveModel::Serializer attributes :id, - :updated_at + :updated_at end diff --git a/app/serializers/entreprise_serializer.rb b/app/serializers/entreprise_serializer.rb index b293b3142..d46ab8672 100644 --- a/app/serializers/entreprise_serializer.rb +++ b/app/serializers/entreprise_serializer.rb @@ -1,14 +1,14 @@ class EntrepriseSerializer < ActiveModel::Serializer attributes :siren, - :capital_social, - :numero_tva_intracommunautaire, - :forme_juridique, - :forme_juridique_code, - :nom_commercial, - :raison_sociale, - :siret_siege_social, - :code_effectif_entreprise, - :date_creation, - :nom, - :prenom + :capital_social, + :numero_tva_intracommunautaire, + :forme_juridique, + :forme_juridique_code, + :nom_commercial, + :raison_sociale, + :siret_siege_social, + :code_effectif_entreprise, + :date_creation, + :nom, + :prenom end diff --git a/app/serializers/etablissement_serializer.rb b/app/serializers/etablissement_serializer.rb index 11b4ce969..6cd091859 100644 --- a/app/serializers/etablissement_serializer.rb +++ b/app/serializers/etablissement_serializer.rb @@ -1,15 +1,14 @@ class EtablissementSerializer < ActiveModel::Serializer attributes :siret, - :siege_social, - :naf, - :libelle_naf, - :adresse, - :numero_voie, - :type_voie, - :nom_voie, - :complement_adresse, - :code_postal, - :localite, - :code_insee_localite - + :siege_social, + :naf, + :libelle_naf, + :adresse, + :numero_voie, + :type_voie, + :nom_voie, + :complement_adresse, + :code_postal, + :localite, + :code_insee_localite end diff --git a/app/serializers/module_api_carto_serializer.rb b/app/serializers/module_api_carto_serializer.rb index 671b754bf..0c993848d 100644 --- a/app/serializers/module_api_carto_serializer.rb +++ b/app/serializers/module_api_carto_serializer.rb @@ -1,7 +1,5 @@ class ModuleApiCartoSerializer < ActiveModel::Serializer attributes :use_api_carto, - :quartiers_prioritaires, - :cadastre - - + :quartiers_prioritaires, + :cadastre end diff --git a/app/serializers/piece_justificative_serializer.rb b/app/serializers/piece_justificative_serializer.rb index 1b460e320..fdcf1d2c9 100644 --- a/app/serializers/piece_justificative_serializer.rb +++ b/app/serializers/piece_justificative_serializer.rb @@ -1,7 +1,7 @@ class PieceJustificativeSerializer < ActiveModel::Serializer attributes :created_at, - :type_de_piece_justificative_id, - :content_url + :type_de_piece_justificative_id, + :content_url has_one :user end diff --git a/app/serializers/procedure_serializer.rb b/app/serializers/procedure_serializer.rb index c85ed7ff4..228a1f710 100644 --- a/app/serializers/procedure_serializer.rb +++ b/app/serializers/procedure_serializer.rb @@ -3,13 +3,12 @@ class ProcedureSerializer < ActiveModel::Serializer attribute :lien_demarche, key: :link attributes :id, - :description, - :organisation, - :direction, - :archived, - :geographic_information, - :total_dossier - + :description, + :organisation, + :direction, + :archived, + :geographic_information, + :total_dossier has_one :geographic_information, serializer: ModuleApiCartoSerializer has_many :types_de_champ, serializer: TypeDeChampSerializer diff --git a/app/serializers/type_de_champ_serializer.rb b/app/serializers/type_de_champ_serializer.rb index bec424797..152e908e1 100644 --- a/app/serializers/type_de_champ_serializer.rb +++ b/app/serializers/type_de_champ_serializer.rb @@ -1,7 +1,7 @@ class TypeDeChampSerializer < ActiveModel::Serializer attributes :id, - :libelle, - :type_champ, - :order_place, - :description + :libelle, + :type_champ, + :order_place, + :description end diff --git a/app/serializers/type_de_piece_justificative_serializer.rb b/app/serializers/type_de_piece_justificative_serializer.rb index cff798508..53ee7cf81 100644 --- a/app/serializers/type_de_piece_justificative_serializer.rb +++ b/app/serializers/type_de_piece_justificative_serializer.rb @@ -1,7 +1,7 @@ class TypeDePieceJustificativeSerializer < ActiveModel::Serializer attributes :id, - :libelle, - :description, - :order_place, - :lien_demarche + :libelle, + :description, + :order_place, + :lien_demarche end diff --git a/spec/controllers/backoffice/preference_list_dossier_controller_spec.rb b/spec/controllers/backoffice/preference_list_dossier_controller_spec.rb index ec365de42..cd6ca6ad7 100644 --- a/spec/controllers/backoffice/preference_list_dossier_controller_spec.rb +++ b/spec/controllers/backoffice/preference_list_dossier_controller_spec.rb @@ -14,11 +14,14 @@ describe Backoffice::PreferenceListDossierController, type: :controller do describe '#POST add' do subject { post :add, - params: {libelle: libelle, - table: table, - attr: attr, - attr_decorate: attr_decorate, - bootstrap_lg: bootstrap_lg} } + params: { + libelle: libelle, + table: table, + attr: attr, + attr_decorate: attr_decorate, + bootstrap_lg: bootstrap_lg + } + } it { expect(subject.status).to eq 200 } it { expect { subject }.to change(PreferenceListDossier, :count).by(1) } diff --git a/spec/controllers/backoffice/private_formulaires_controller_spec.rb b/spec/controllers/backoffice/private_formulaires_controller_spec.rb index 2ff55d3bd..65e44d8ec 100644 --- a/spec/controllers/backoffice/private_formulaires_controller_spec.rb +++ b/spec/controllers/backoffice/private_formulaires_controller_spec.rb @@ -13,10 +13,13 @@ describe Backoffice::PrivateFormulairesController, type: :controller do describe '#PATCH update' do subject { patch :update, - params: {dossier_id: dossier.id, - champs: { - "'#{dossier.champs_private.first.id}'" => dossier_champs_first - }} } + params: { + dossier_id: dossier.id, + champs: { + "'#{dossier.champs_private.first.id}'" => dossier_champs_first + } + } + } before do subject diff --git a/spec/facades/dossiers_list_facades_spec.rb b/spec/facades/dossiers_list_facades_spec.rb index 8713df086..f8ed7e2fa 100644 --- a/spec/facades/dossiers_list_facades_spec.rb +++ b/spec/facades/dossiers_list_facades_spec.rb @@ -7,17 +7,17 @@ describe DossiersListFacades do let(:procedure_2) { create :procedure, libelle: 'Ma seconde procédure' } let!(:preference) { create :preference_list_dossier, - gestionnaire: gestionnaire, - table: nil, - attr: 'state', - attr_decorate: 'display_state' } + gestionnaire: gestionnaire, + table: nil, + attr: 'state', + attr_decorate: 'display_state' } let!(:preference_2) { create :preference_list_dossier, - gestionnaire: gestionnaire, - table: 'champs', - attr: 'state', - attr_decorate: 'display_state', - procedure_id: procedure.id } + gestionnaire: gestionnaire, + table: 'champs', + attr: 'state', + attr_decorate: 'display_state', + procedure_id: procedure.id } before do create :assign_to, procedure: procedure, gestionnaire: gestionnaire @@ -58,12 +58,12 @@ describe DossiersListFacades do let(:facade) { described_class.new gestionnaire, 'nouveaux', procedure_2 } let!(:preference) { create :preference_list_dossier, - gestionnaire: gestionnaire, - table: table, - attr: 'state', - attr_decorate: 'display_state', - filter: filter, - procedure_id: procedure_id } + gestionnaire: gestionnaire, + table: table, + attr: 'state', + attr_decorate: 'display_state', + filter: filter, + procedure_id: procedure_id } subject { facade.active_filter? preference } @@ -99,12 +99,12 @@ describe DossiersListFacades do before do create :preference_list_dossier, - gestionnaire: gestionnaire, - table: 'champs', - attr: 'state', - attr_decorate: 'display_state', - filter: 'plop', - procedure_id: procedure_id + gestionnaire: gestionnaire, + table: 'champs', + attr: 'state', + attr_decorate: 'display_state', + filter: 'plop', + procedure_id: procedure_id end it { is_expected.to be_falsey } diff --git a/spec/features/france_connect/france_connect_particulier_spec.rb b/spec/features/france_connect/france_connect_particulier_spec.rb index 1ed8ca15f..2ba15e71b 100644 --- a/spec/features/france_connect/france_connect_particulier_spec.rb +++ b/spec/features/france_connect/france_connect_particulier_spec.rb @@ -34,13 +34,13 @@ feature 'France Connect Particulier Connexion' do context 'when authentification is ok' do let(:france_connect_information) { create(:france_connect_information, - france_connect_particulier_id: france_connect_particulier_id, - given_name: given_name, - family_name: family_name, - birthdate: birthdate, - birthplace: birthplace, - gender: gender, - email_france_connect: email) } + france_connect_particulier_id: france_connect_particulier_id, + given_name: given_name, + family_name: family_name, + birthdate: birthdate, + birthplace: birthplace, + gender: gender, + email_france_connect: email) } before do allow_any_instance_of(FranceConnectParticulierClient).to receive(:authorization_uri).and_return(france_connect_particulier_callback_path(code: code)) diff --git a/spec/services/dossiers_list_gestionnaire_service_spec.rb b/spec/services/dossiers_list_gestionnaire_service_spec.rb index d45293868..f1ca2b6e2 100644 --- a/spec/services/dossiers_list_gestionnaire_service_spec.rb +++ b/spec/services/dossiers_list_gestionnaire_service_spec.rb @@ -231,12 +231,12 @@ describe DossiersListGestionnaireService do context 'when preference list contain a champ' do before do create :preference_list_dossier, - gestionnaire: gestionnaire, - table: 'champs', - attr: '34', - attr_decorate: '', - filter: 'plop', - procedure_id: create(:procedure) + gestionnaire: gestionnaire, + table: 'champs', + attr: '34', + attr_decorate: '', + filter: 'plop', + procedure_id: create(:procedure) end it { is_expected.to eq "CAST(dossiers.id as TEXT) LIKE '%23%' AND CAST(entreprises.raison_sociale as TEXT) LIKE '%plop%' AND champs.type_de_champ_id = 34 AND CAST(champs.value as TEXT) LIKE '%plop%'" } diff --git a/spec/views/admin/accompagnateurs/show.html.haml_spec.rb b/spec/views/admin/accompagnateurs/show.html.haml_spec.rb index 4593795ec..231b6f05a 100644 --- a/spec/views/admin/accompagnateurs/show.html.haml_spec.rb +++ b/spec/views/admin/accompagnateurs/show.html.haml_spec.rb @@ -12,14 +12,14 @@ describe 'admin/accompagnateurs/show.html.haml', type: :view do assign(:gestionnaire, Gestionnaire.new) assign(:accompagnateurs_assign, (smart_listing_create :accompagnateurs_assign, - assign_gestionnaires, - partial: "admin/accompagnateurs/list_assign", - array: true)) + assign_gestionnaires, + partial: "admin/accompagnateurs/list_assign", + array: true)) assign(:accompagnateurs_not_assign, (smart_listing_create :accompagnateurs_not_assign, - not_assign_gestionnaires, - partial: "admin/accompagnateurs/list_not_assign", - array: true)) + not_assign_gestionnaires, + partial: "admin/accompagnateurs/list_not_assign", + array: true)) end context 'when admin have none accompagnateur ' do @@ -43,14 +43,14 @@ describe 'admin/accompagnateurs/show.html.haml', type: :view do assign_gestionnaires.reload assign(:accompagnateurs_assign, (smart_listing_create :accompagnateurs_assign, - assign_gestionnaires, - partial: "admin/accompagnateurs/list_assign", - array: true)) + assign_gestionnaires, + partial: "admin/accompagnateurs/list_assign", + array: true)) assign(:accompagnateurs_not_assign, (smart_listing_create :accompagnateurs_not_assign, - not_assign_gestionnaires, - partial: "admin/accompagnateurs/list_not_assign", - array: true)) + not_assign_gestionnaires, + partial: "admin/accompagnateurs/list_not_assign", + array: true)) render end diff --git a/spec/views/admin/gestionnaires/index.html.haml_spec.rb b/spec/views/admin/gestionnaires/index.html.haml_spec.rb index 7f8820abd..be3014619 100644 --- a/spec/views/admin/gestionnaires/index.html.haml_spec.rb +++ b/spec/views/admin/gestionnaires/index.html.haml_spec.rb @@ -6,9 +6,9 @@ describe 'admin/gestionnaires/index.html.haml', type: :view do before do assign(:gestionnaires, (smart_listing_create :gestionnaires, - admin.gestionnaires, - partial: "admin/gestionnaires/list", - array: true)) + admin.gestionnaires, + partial: "admin/gestionnaires/list", + array: true)) assign(:gestionnaire, Gestionnaire.new()) end @@ -24,9 +24,9 @@ describe 'admin/gestionnaires/index.html.haml', type: :view do create(:gestionnaire, administrateurs: [admin]) admin.reload assign(:gestionnaires, (smart_listing_create :gestionnaires, - admin.gestionnaires, - partial: "admin/gestionnaires/list", - array: true)) + admin.gestionnaires, + partial: "admin/gestionnaires/list", + array: true)) render end it { expect(rendered).to match(/gest\d+@gest.com/) } diff --git a/spec/views/admin/previsualisations/show.html.haml_spec.rb b/spec/views/admin/previsualisations/show.html.haml_spec.rb index cfed1dd02..ffca23efe 100644 --- a/spec/views/admin/previsualisations/show.html.haml_spec.rb +++ b/spec/views/admin/previsualisations/show.html.haml_spec.rb @@ -65,8 +65,7 @@ describe 'admin/previsualisations/show.html.haml', type: :view do context 'les valeurs sont réaffichées si elles sont présentes dans la BDD' do let!(:dossier) do - create(:dossier, - user: user) + create(:dossier, user: user) end before do diff --git a/spec/views/backoffice/dossiers/index_html.haml_spec.rb b/spec/views/backoffice/dossiers/index_html.haml_spec.rb index 4951907d1..307fa0113 100644 --- a/spec/views/backoffice/dossiers/index_html.haml_spec.rb +++ b/spec/views/backoffice/dossiers/index_html.haml_spec.rb @@ -29,28 +29,28 @@ describe 'backoffice/dossiers/index.html.haml', type: :view do decorate_dossier_without_continuation.entreprise.update_column(:raison_sociale, 'plnp') create :preference_list_dossier, - gestionnaire: gestionnaire, - table: nil, - attr: 'state', - attr_decorate: 'display_state' + gestionnaire: gestionnaire, + table: nil, + attr: 'state', + attr_decorate: 'display_state' create :preference_list_dossier, - gestionnaire: gestionnaire, - table: 'procedure', - attr: 'libelle', - attr_decorate: 'libelle' + gestionnaire: gestionnaire, + table: 'procedure', + attr: 'libelle', + attr_decorate: 'libelle' create :preference_list_dossier, - gestionnaire: gestionnaire, - table: 'entreprise', - attr: 'raison_sociale', - attr_decorate: 'raison_sociale' + gestionnaire: gestionnaire, + table: 'entreprise', + attr: 'raison_sociale', + attr_decorate: 'raison_sociale' create :preference_list_dossier, - gestionnaire: gestionnaire, - table: nil, - attr: 'last_update', - attr_decorate: 'last_update' + gestionnaire: gestionnaire, + table: nil, + attr: 'last_update', + attr_decorate: 'last_update' create :assign_to, gestionnaire: gestionnaire, procedure: procedure sign_in gestionnaire @@ -58,19 +58,19 @@ describe 'backoffice/dossiers/index.html.haml', type: :view do assign :facade_data_view, dossiers_list_facade assign(:new_dossiers, (smart_listing_create :new_dossiers, - new_dossiers_list, - partial: "backoffice/dossiers/list", - array: true)) + new_dossiers_list, + partial: "backoffice/dossiers/list", + array: true)) assign(:follow_dossiers, (smart_listing_create :follow_dossiers, - follow_dossiers_list, - partial: "backoffice/dossiers/list", - array: true)) + follow_dossiers_list, + partial: "backoffice/dossiers/list", + array: true)) assign(:all_state_dossiers, (smart_listing_create :all_state_dossiers, - all_state_dossiers_list, - partial: "backoffice/dossiers/list", - array: true)) + all_state_dossiers_list, + partial: "backoffice/dossiers/list", + array: true)) render end diff --git a/spec/views/users/dossiers/index_html.haml_spec.rb b/spec/views/users/dossiers/index_html.haml_spec.rb index 3813456f8..dc6721b41 100644 --- a/spec/views/users/dossiers/index_html.haml_spec.rb +++ b/spec/views/users/dossiers/index_html.haml_spec.rb @@ -22,9 +22,9 @@ describe 'users/dossiers/index.html.haml', type: :view do assign :dossiers_list_facade, (DossiersListFacades.new user, liste) assign(:dossiers, (smart_listing_create :dossiers, - dossiers_to_display, - partial: "users/dossiers/list", - array: true)) + dossiers_to_display, + partial: "users/dossiers/list", + array: true)) render end From bc16027ccc869ddbf0f97ea716ad823c2f1cb5ba Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 14:10:49 +0200 Subject: [PATCH 009/136] Enable the Layout/BlockEndNewline cop --- .rubocop.yml | 2 +- app/services/module_api_carto_service.rb | 6 ++-- .../api/v1/dossiers_controller_spec.rb | 33 +++++++++++-------- spec/controllers/stats_controller_spec.rb | 20 ++++++----- .../description_controller_shared_example.rb | 9 +++-- spec/facades/dossiers_list_facades_spec.rb | 9 +++-- .../france_connect_particulier_spec.rb | 6 ++-- spec/lib/carto/sgmap/cadastre/adapter_spec.rb | 3 +- spec/models/drop_down_list_spec.rb | 9 +++-- spec/models/gestionnaire_spec.rb | 9 +++-- ...dossiers_list_gestionnaire_service_spec.rb | 6 ++-- .../_pieces_justificatives.html.haml_spec.rb | 32 ++++++++++-------- 12 files changed, 89 insertions(+), 55 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 9a19249af..78b587ff4 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -18,7 +18,7 @@ Layout/AlignParameters: EnforcedStyle: with_fixed_indentation Layout/BlockEndNewline: - Enabled: false + Enabled: true Layout/CaseIndentation: Enabled: false diff --git a/app/services/module_api_carto_service.rb b/app/services/module_api_carto_service.rb index 4838d13b1..d32307e87 100644 --- a/app/services/module_api_carto_service.rb +++ b/app/services/module_api_carto_service.rb @@ -26,12 +26,14 @@ class ModuleApiCartoService def self.generate_qp coordinates coordinates.inject({}) { |acc, coordinate| acc.merge CARTO::SGMAP::QuartiersPrioritaires::Adapter.new( - coordinate.map { |element| [element['lng'], element['lat']] }).to_params } + coordinate.map { |element| [element['lng'], element['lat']] }).to_params + } end def self.generate_cadastre coordinates (coordinates.inject([]) { |acc, coordinate| acc << CARTO::SGMAP::Cadastre::Adapter.new( - coordinate.map { |element| [element['lng'], element['lat']] }).to_params }).flatten + coordinate.map { |element| [element['lng'], element['lat']] }).to_params + }).flatten end end diff --git a/spec/controllers/api/v1/dossiers_controller_spec.rb b/spec/controllers/api/v1/dossiers_controller_spec.rb index be00999ee..bcbf27315 100644 --- a/spec/controllers/api/v1/dossiers_controller_spec.rb +++ b/spec/controllers/api/v1/dossiers_controller_spec.rb @@ -144,7 +144,8 @@ describe API::V1::DossiersController do :code_effectif_entreprise, :date_creation, :nom, - :prenom] } + :prenom] + } subject { super()[:entreprise] } it { expect(subject[:siren]).to eq('440117620') } @@ -164,7 +165,8 @@ describe API::V1::DossiersController do let(:field_list) { [ :id, :libelle, - :description] } + :description] + } subject { super()[:types_de_piece_justificative] } it { expect(subject.length).to eq 2 } @@ -184,9 +186,9 @@ describe API::V1::DossiersController do end let(:field_list) { [ - :url, :created_at, :type_de_piece_justificative_id] } - subject { - super()[:pieces_justificatives].first } + :url, :created_at, :type_de_piece_justificative_id] + } + subject { super()[:pieces_justificatives].first } it { expect(subject.keys.include?(:content_url)).to be_truthy } it { expect(subject[:created_at]).not_to be_nil } @@ -203,7 +205,8 @@ describe API::V1::DossiersController do describe 'champs' do let(:field_list) { [ - :url] } + :url] + } subject { super()[:champs] } it { expect(subject.length).to eq 1 } @@ -220,7 +223,8 @@ describe API::V1::DossiersController do :libelle, :description, :order_place, - :type] } + :type] + } subject { super()[:type_de_champ] } it { expect(subject.keys.include?(:id)).to be_truthy } @@ -234,7 +238,8 @@ describe API::V1::DossiersController do describe 'champs_private' do let(:field_list) { [ - :url] } + :url] + } subject { super()[:champs_private] } it { expect(subject.length).to eq 1 } @@ -251,7 +256,8 @@ describe API::V1::DossiersController do :libelle, :description, :order_place, - :type] } + :type] + } subject { super()[:type_de_champ] } it { expect(subject.keys.include?(:id)).to be_truthy } @@ -298,9 +304,9 @@ describe API::V1::DossiersController do describe 'user' do let(:field_list) { [ - :url, :created_at, :type_de_piece_justificative_id] } - subject { - super()[:user] } + :url, :created_at, :type_de_piece_justificative_id] + } + subject { super()[:user] } it { expect(subject[:email]).not_to be_nil } end @@ -320,7 +326,8 @@ describe API::V1::DossiersController do :code_postal, :localite, :code_insee_localite - ] } + ] + } subject { super()[:etablissement] } it { expect(subject[:siret]).to eq('44011762001530') } diff --git a/spec/controllers/stats_controller_spec.rb b/spec/controllers/stats_controller_spec.rb index d574fde07..238af0f66 100644 --- a/spec/controllers/stats_controller_spec.rb +++ b/spec/controllers/stats_controller_spec.rb @@ -17,7 +17,8 @@ describe StatsController, type: :controller do it { expect(subject).to eq([ [I18n.l(45.days.ago.beginning_of_month, format: "%B %Y"), 1], [I18n.l(1.days.ago.beginning_of_month, format: "%B %Y"), 2] - ] ) } + ]) + } end context "with a date attribute" do @@ -35,7 +36,8 @@ describe StatsController, type: :controller do it { expect(subject).to eq([ [I18n.l(45.days.ago.beginning_of_month, format: "%B %Y"), 2], [I18n.l(1.days.ago.beginning_of_month, format: "%B %Y"), 1] - ] ) } + ]) + } end end @@ -52,9 +54,10 @@ describe StatsController, type: :controller do subject { StatsController.new.send(:cumulative_hash, association) } it { expect(subject).to eq({ - 45.days.ago.beginning_of_month => 1, - 15.days.ago.beginning_of_month => 3 - }) } + 45.days.ago.beginning_of_month => 1, + 15.days.ago.beginning_of_month => 3 + }) + } end context "with a date attribute" do @@ -69,9 +72,10 @@ describe StatsController, type: :controller do subject { StatsController.new.send(:cumulative_hash, association, :updated_at) } it { expect(subject).to eq({ - 20.days.ago.beginning_of_month => 2, - 10.days.ago.beginning_of_month => 3 - }) } + 20.days.ago.beginning_of_month => 2, + 10.days.ago.beginning_of_month => 3 + }) + } end end diff --git a/spec/controllers/users/description_controller_shared_example.rb b/spec/controllers/users/description_controller_shared_example.rb index f74eb29b1..9a73c5735 100644 --- a/spec/controllers/users/description_controller_shared_example.rb +++ b/spec/controllers/users/description_controller_shared_example.rb @@ -161,7 +161,8 @@ shared_examples 'description_controller_spec' do context 'Quand la procédure accepte les CERFA' do subject { post :update, params: {dossier_id: dossier_id, - cerfa_pdf: cerfa_pdf} } + cerfa_pdf: cerfa_pdf} + } it 'Notification interne is create' do expect { subject }.to change(Notification, :count).by (1) @@ -319,7 +320,8 @@ shared_examples 'description_controller_spec' do subject { patch :pieces_justificatives, params: {dossier_id: dossier.id, 'piece_justificative_'+all_pj_type[0].to_s => piece_justificative_0, - 'piece_justificative_'+all_pj_type[1].to_s => piece_justificative_1} } + 'piece_justificative_'+all_pj_type[1].to_s => piece_justificative_1} + } context 'when user is a guest' do let(:guest) { create :user } @@ -394,7 +396,8 @@ shared_examples 'description_controller_spec_POST_piece_justificatives_for_owner subject { patch :pieces_justificatives, params: {dossier_id: dossier.id, 'piece_justificative_'+all_pj_type[0].to_s => piece_justificative_0, - 'piece_justificative_'+all_pj_type[1].to_s => piece_justificative_1} } + 'piece_justificative_'+all_pj_type[1].to_s => piece_justificative_1} + } context 'when user is the owner', vcr: {cassette_name: 'controllers_users_description_controller_pieces_justificatives'} do before do diff --git a/spec/facades/dossiers_list_facades_spec.rb b/spec/facades/dossiers_list_facades_spec.rb index f8ed7e2fa..0a9c7a0d3 100644 --- a/spec/facades/dossiers_list_facades_spec.rb +++ b/spec/facades/dossiers_list_facades_spec.rb @@ -10,14 +10,16 @@ describe DossiersListFacades do gestionnaire: gestionnaire, table: nil, attr: 'state', - attr_decorate: 'display_state' } + attr_decorate: 'display_state' + } let!(:preference_2) { create :preference_list_dossier, gestionnaire: gestionnaire, table: 'champs', attr: 'state', attr_decorate: 'display_state', - procedure_id: procedure.id } + procedure_id: procedure.id + } before do create :assign_to, procedure: procedure, gestionnaire: gestionnaire @@ -63,7 +65,8 @@ describe DossiersListFacades do attr: 'state', attr_decorate: 'display_state', filter: filter, - procedure_id: procedure_id } + procedure_id: procedure_id + } subject { facade.active_filter? preference } diff --git a/spec/features/france_connect/france_connect_particulier_spec.rb b/spec/features/france_connect/france_connect_particulier_spec.rb index 2ba15e71b..781e35b6c 100644 --- a/spec/features/france_connect/france_connect_particulier_spec.rb +++ b/spec/features/france_connect/france_connect_particulier_spec.rb @@ -17,7 +17,8 @@ feature 'France Connect Particulier Connexion' do birthdate: birthdate, birthplace: birthplace, gender: gender, - email: email) } + email: email) + } context 'when user is on login page' do @@ -40,7 +41,8 @@ feature 'France Connect Particulier Connexion' do birthdate: birthdate, birthplace: birthplace, gender: gender, - email_france_connect: email) } + email_france_connect: email) + } before do allow_any_instance_of(FranceConnectParticulierClient).to receive(:authorization_uri).and_return(france_connect_particulier_callback_path(code: code)) diff --git a/spec/lib/carto/sgmap/cadastre/adapter_spec.rb b/spec/lib/carto/sgmap/cadastre/adapter_spec.rb index 134675d3a..e6912c1cb 100644 --- a/spec/lib/carto/sgmap/cadastre/adapter_spec.rb +++ b/spec/lib/carto/sgmap/cadastre/adapter_spec.rb @@ -31,7 +31,8 @@ describe CARTO::SGMAP::Cadastre::Adapter do :code_dep, :nom_com, :code_com, - :code_arr] } + :code_arr] + } end describe 'Attributes' do diff --git a/spec/models/drop_down_list_spec.rb b/spec/models/drop_down_list_spec.rb index 0455985fc..b065dace9 100644 --- a/spec/models/drop_down_list_spec.rb +++ b/spec/models/drop_down_list_spec.rb @@ -17,7 +17,8 @@ describe DropDownList do Dév.Eco / Emploi Cadre de vie / Urb. Pilotage / Ingénierie -" } +" +} it { expect(dropdownlist.options).to eq ['', 'Cohésion sociale', 'Dév.Eco / Emploi', 'Cadre de vie / Urb.', 'Pilotage / Ingénierie'] } @@ -26,7 +27,8 @@ Pilotage / Ingénierie Cadre de vie / Urb. Pilotage / Ingénierie -" } +" +} it { expect(dropdownlist.options).to eq ['', 'Cohésion sociale', 'Cadre de vie / Urb.', 'Pilotage / Ingénierie'] } end @@ -36,7 +38,8 @@ Pilotage / Ingénierie let(:value) { "tip --top-- --troupt-- -ouaich" } +ouaich" +} it { expect(dropdownlist.disabled_options).to match(['--top--', '--troupt--']) } end diff --git a/spec/models/gestionnaire_spec.rb b/spec/models/gestionnaire_spec.rb index fbfbfd76f..8b85128d4 100644 --- a/spec/models/gestionnaire_spec.rb +++ b/spec/models/gestionnaire_spec.rb @@ -204,7 +204,8 @@ describe Gestionnaire, type: :model do it { is_expected.to eq 0 } it { expect(gestionnaire.follows.count).to eq 0 } it { expect_any_instance_of(Dossier::ActiveRecord_AssociationRelation).not_to receive(:inject) - subject } + subject + } end context 'when gestionnaire follow any dossier into the procedure past in params' do @@ -215,7 +216,8 @@ describe Gestionnaire, type: :model do it { is_expected.to eq 0 } it { expect(gestionnaire.follows.count).to eq 1 } it { expect_any_instance_of(Dossier::ActiveRecord_AssociationRelation).not_to receive(:inject) - subject } + subject + } end context 'when gestionnaire follow a dossier with a notification into the procedure past in params' do @@ -229,7 +231,8 @@ describe Gestionnaire, type: :model do it { is_expected.to eq 1 } it { expect(gestionnaire.follows.count).to eq 1 } it { expect_any_instance_of(Dossier::ActiveRecord_AssociationRelation).to receive(:inject) - subject } + subject + } end end diff --git a/spec/services/dossiers_list_gestionnaire_service_spec.rb b/spec/services/dossiers_list_gestionnaire_service_spec.rb index f1ca2b6e2..de32a0aa9 100644 --- a/spec/services/dossiers_list_gestionnaire_service_spec.rb +++ b/spec/services/dossiers_list_gestionnaire_service_spec.rb @@ -49,7 +49,8 @@ describe DossiersListGestionnaireService do let(:order) { 'desc' } let(:select_preference_list_dossier) { gestionnaire.preference_list_dossiers - .find_by(table: table, attr: attr, procedure: nil) } + .find_by(table: table, attr: attr, procedure: nil) + } subject { DossiersListGestionnaireService.new(gestionnaire, liste).change_sort! param_sort } @@ -99,7 +100,8 @@ describe DossiersListGestionnaireService do let(:filter_value) { 'plop' } let(:select_preference_list_dossier) { gestionnaire.preference_list_dossiers - .find_by(table: table, attr: attr, procedure: nil) } + .find_by(table: table, attr: attr, procedure: nil) + } subject { described_class.new(gestionnaire, liste).add_filter new_filter } diff --git a/spec/views/users/description/_pieces_justificatives.html.haml_spec.rb b/spec/views/users/description/_pieces_justificatives.html.haml_spec.rb index d0bcacd86..410d63c74 100644 --- a/spec/views/users/description/_pieces_justificatives.html.haml_spec.rb +++ b/spec/views/users/description/_pieces_justificatives.html.haml_spec.rb @@ -2,20 +2,24 @@ require 'spec_helper' describe 'users/description/_pieces_justificatives.html.haml', type: :view do let!(:procedure) { create(:procedure) } - let!(:tpj1) { create(:type_de_piece_justificative, - procedure: procedure, - libelle: "Première pièce jointe", - description: "Première description", - order_place: 1, - mandatory: true - )} - let!(:tpj2) { create(:type_de_piece_justificative, - procedure: procedure, - libelle: "Seconde pièce jointe", - description: "Seconde description", - order_place: 2, - lien_demarche: "https://www.google.fr" - )} + let!(:tpj1) { + create(:type_de_piece_justificative, + procedure: procedure, + libelle: "Première pièce jointe", + description: "Première description", + order_place: 1, + mandatory: true + ) + } + let!(:tpj2) { + create(:type_de_piece_justificative, + procedure: procedure, + libelle: "Seconde pièce jointe", + description: "Seconde description", + order_place: 2, + lien_demarche: "https://www.google.fr" + ) + } let!(:dossier) { create(:dossier, :procedure => procedure) } before do From 1b78a8e95771a99e64c13358a04b0343175b306c Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 15:05:46 +0200 Subject: [PATCH 010/136] Enable the Layout/CaseIndentation cop --- .rubocop.yml | 3 ++- app/services/notification_service.rb | 28 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 78b587ff4..73e8ae0a7 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -21,7 +21,8 @@ Layout/BlockEndNewline: Enabled: true Layout/CaseIndentation: - Enabled: false + Enabled: true + EnforcedStyle: end Layout/ClosingParenthesisIndentation: Enabled: true diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index a401064cf..27abe24cf 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -25,20 +25,20 @@ class NotificationService def text_for_notif attribut='' case @type_notif - when 'commentaire' - "#{notification.liste.size + 1} nouveau(x) commentaire(s) déposé(s)." - when 'cerfa' - "Un nouveau formulaire a été déposé." - when 'piece_justificative' - attribut - when 'champs' - attribut - when 'submitted' - "Le dossier nº #{@dossier_id} a été déposé." - when 'avis' - 'Un nouvel avis a été rendu' - else - 'Notification par défaut' + when 'commentaire' + "#{notification.liste.size + 1} nouveau(x) commentaire(s) déposé(s)." + when 'cerfa' + "Un nouveau formulaire a été déposé." + when 'piece_justificative' + attribut + when 'champs' + attribut + when 'submitted' + "Le dossier nº #{@dossier_id} a été déposé." + when 'avis' + 'Un nouvel avis a été rendu' + else + 'Notification par défaut' end end end From f2cf32a15c1301d581d2fc6d4be6b9839b7350b7 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 15:12:51 +0200 Subject: [PATCH 011/136] Enable the Layout/CommentIndentation cop --- .rubocop.yml | 2 +- .../users/registrations_controller.rb | 4 +- app/controllers/users/sessions_controller.rb | 8 +- app/uploaders/cerfa_uploader.rb | 2 +- config/initializers/smart_listing.rb | 132 +++++++++--------- 5 files changed, 74 insertions(+), 74 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 73e8ae0a7..61b54a3ea 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -28,7 +28,7 @@ Layout/ClosingParenthesisIndentation: Enabled: true Layout/CommentIndentation: - Enabled: false + Enabled: true Layout/DotPosition: Enabled: false diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb index fbb10dc8a..1a231b900 100644 --- a/app/controllers/users/registrations_controller.rb +++ b/app/controllers/users/registrations_controller.rb @@ -1,6 +1,6 @@ class Users::RegistrationsController < Devise::RegistrationsController -# before_action :configure_sign_up_params, only: [:create] -# before_action :configure_account_update_params, only: [:update] + # before_action :configure_sign_up_params, only: [:create] + # before_action :configure_account_update_params, only: [:update] def after_sign_up_path_for(resource_or_scope) WelcomeMailer.welcome_email(resource_or_scope).deliver_now! diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index 2a990beaf..82eb76ad7 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -1,5 +1,5 @@ class Users::SessionsController < Sessions::SessionsController -# before_action :configure_sign_in_params, only: [:create] + # before_action :configure_sign_in_params, only: [:create] def demo return redirect_to root_path if Rails.env.production? @@ -8,7 +8,7 @@ class Users::SessionsController < Sessions::SessionsController render 'new' end -# GET /resource/sign_in + # GET /resource/sign_in def new unless user_return_to_procedure_id.nil? # WTF ? @dossier = Dossier.new(procedure: Procedure.active(user_return_to_procedure_id)) @@ -19,7 +19,7 @@ class Users::SessionsController < Sessions::SessionsController error_procedure end -#POST /resource/sign_in + #POST /resource/sign_in def create remember_me = params[:user][:remember_me] == '1' try_to_authenticate(User, remember_me) @@ -44,7 +44,7 @@ class Users::SessionsController < Sessions::SessionsController end end -# DELETE /resource/sign_out + # DELETE /resource/sign_out def destroy sign_out :gestionnaire if gestionnaire_signed_in? sign_out :administrateur if administrateur_signed_in? diff --git a/app/uploaders/cerfa_uploader.rb b/app/uploaders/cerfa_uploader.rb index c398801b8..7d31762f5 100644 --- a/app/uploaders/cerfa_uploader.rb +++ b/app/uploaders/cerfa_uploader.rb @@ -3,7 +3,7 @@ class CerfaUploader < BaseUploader before :cache, :set_original_filename -# Choose what kind of storage to use for this uploader: + # Choose what kind of storage to use for this uploader: if Features.remote_storage storage :fog else diff --git a/config/initializers/smart_listing.rb b/config/initializers/smart_listing.rb index 90e3f99f9..91d459d6d 100644 --- a/config/initializers/smart_listing.rb +++ b/config/initializers/smart_listing.rb @@ -1,75 +1,75 @@ SmartListing.configure do |config| config.global_options({ - #:param_names => { # param names - #:page => :page, - #:per_page => :per_page, - #:sort => :sort, - #}, - #:array => false, # controls whether smart list should be using arrays or AR collections - #:max_count => nil, # limit number of rows - #:unlimited_per_page => false, # allow infinite page size - #:paginate => true, # allow pagination - #:memorize_per_page => false, # save per page settings in the cookie - :page_sizes => [10, 20, 50, 100], # set available page sizes array - #:kaminari_options => {:theme => "smart_listing"}, # Kaminari's paginate helper options - }) + # :param_names => { # param names + # :page => :page, + # :per_page => :per_page, + # :sort => :sort, + # }, + # :array => false, # controls whether smart list should be using arrays or AR collections + # :max_count => nil, # limit number of rows + # :unlimited_per_page => false, # allow infinite page size + # :paginate => true, # allow pagination + # :memorize_per_page => false, # save per page settings in the cookie + :page_sizes => [10, 20, 50, 100], # set available page sizes array + # :kaminari_options => {:theme => "smart_listing"}, # Kaminari's paginate helper options + }) config.constants :classes, { - #:main => "smart-listing", - #:editable => "editable", - #:content => "content", - #:loading => "loading", - #:status => "smart-listing-status", - #:item_actions => "actions", - #:new_item_placeholder => "new-item-placeholder", - #:new_item_action => "new-item-action", - #:new_item_button => "btn", - #:hidden => "hidden", - #:autoselect => "autoselect", - #:callback => "callback", - #:pagination_per_page => "pagination-per-page text-center", - #:pagination_count => "count", - #:inline_editing => "info", - #:no_records => "no-records", - #:limit => "smart-listing-limit", - #:limit_alert => "smart-listing-limit-alert", - #:controls => "smart-listing-controls", - #:controls_reset => "reset", - #:filtering => "filter", - #:filtering_search => "glyphicon-search", - #:filtering_cancel => "glyphicon-remove", - #:filtering_disabled => "disabled", - #:sortable => "sortable", - #:icon_new => "glyphicon glyphicon-plus", - #:icon_edit => "glyphicon glyphicon-pencil", - #:icon_trash => "glyphicon glyphicon-trash", - #:icon_inactive => "glyphicon glyphicon-circle", - #:icon_show => "glyphicon glyphicon-share-alt", - #:icon_sort_none => "glyphicon glyphicon-resize-vertical", - #:icon_sort_up => "glyphicon glyphicon-chevron-up", - #:icon_sort_down => "glyphicon glyphicon-chevron-down", - } + #:main => "smart-listing", + #:editable => "editable", + #:content => "content", + #:loading => "loading", + #:status => "smart-listing-status", + #:item_actions => "actions", + #:new_item_placeholder => "new-item-placeholder", + #:new_item_action => "new-item-action", + #:new_item_button => "btn", + #:hidden => "hidden", + #:autoselect => "autoselect", + #:callback => "callback", + #:pagination_per_page => "pagination-per-page text-center", + #:pagination_count => "count", + #:inline_editing => "info", + #:no_records => "no-records", + #:limit => "smart-listing-limit", + #:limit_alert => "smart-listing-limit-alert", + #:controls => "smart-listing-controls", + #:controls_reset => "reset", + #:filtering => "filter", + #:filtering_search => "glyphicon-search", + #:filtering_cancel => "glyphicon-remove", + #:filtering_disabled => "disabled", + #:sortable => "sortable", + #:icon_new => "glyphicon glyphicon-plus", + #:icon_edit => "glyphicon glyphicon-pencil", + #:icon_trash => "glyphicon glyphicon-trash", + #:icon_inactive => "glyphicon glyphicon-circle", + #:icon_show => "glyphicon glyphicon-share-alt", + #:icon_sort_none => "glyphicon glyphicon-resize-vertical", + #:icon_sort_up => "glyphicon glyphicon-chevron-up", + #:icon_sort_down => "glyphicon glyphicon-chevron-down", + } config.constants :data_attributes, { - #:main => "smart-listing", - #:confirmation => "confirmation", - #:id => "id", - #:href => "href", - #:callback_href => "callback-href", - #:max_count => "max-count", - #:inline_edit_backup => "smart-listing-edit-backup", - #:params => "params", - #:observed => "observed", - #:href => "href", - #:autoshow => "autoshow", - #:popover => "slpopover", - } + # :main => "smart-listing", + # :confirmation => "confirmation", + # :id => "id", + # :href => "href", + # :callback_href => "callback-href", + # :max_count => "max-count", + # :inline_edit_backup => "smart-listing-edit-backup", + # :params => "params", + # :observed => "observed", + # :href => "href", + # :autoshow => "autoshow", + # :popover => "slpopover", + } config.constants :selectors, { - #:item_action_destroy => "a.destroy", - #:edit_cancel => "button.cancel", - #:row => "tr", - #:head => "thead", - #:filtering_icon => "i" - } + # :item_action_destroy => "a.destroy", + # :edit_cancel => "button.cancel", + # :row => "tr", + # :head => "thead", + # :filtering_icon => "i" + } end From a62d5322b6f95fc310a8d496b4d6d4049746c482 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 15:16:15 +0200 Subject: [PATCH 012/136] Enable the Layout/DotPosition cop --- .rubocop.yml | 2 +- spec/controllers/root_controller_spec.rb | 4 ++-- .../users/carte_controller_shared_example.rb | 18 +++++++++--------- spec/features/admin/connection_spec.rb | 4 ++-- spec/lib/carto/sgmap/api_spec.rb | 16 ++++++++-------- spec/lib/carto/sgmap/cadastre/adapter_spec.rb | 8 ++++---- .../quartiers_prioritaires/adapter_spec.rb | 8 ++++---- 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 61b54a3ea..3d4951b13 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -31,7 +31,7 @@ Layout/CommentIndentation: Enabled: true Layout/DotPosition: - Enabled: false + Enabled: true Layout/ElseAlignment: Enabled: true diff --git a/spec/controllers/root_controller_spec.rb b/spec/controllers/root_controller_spec.rb index 9b0b7ed56..3109f46ef 100644 --- a/spec/controllers/root_controller_spec.rb +++ b/spec/controllers/root_controller_spec.rb @@ -57,8 +57,8 @@ describe RootController, type: :controller do render_views before do - stub_request(:get, "https://api.github.com/repos/sgmap/tps/releases/latest"). - to_return(:status => 200, :body => '{"tag_name": "plip", "body": "blabla", "published_at": "2016-02-09T16:46:47Z"}', :headers => {}) + stub_request(:get, "https://api.github.com/repos/sgmap/tps/releases/latest") + .to_return(:status => 200, :body => '{"tag_name": "plip", "body": "blabla", "published_at": "2016-02-09T16:46:47Z"}', :headers => {}) subject end diff --git a/spec/controllers/users/carte_controller_shared_example.rb b/spec/controllers/users/carte_controller_shared_example.rb index 55d63d266..5208fc3e7 100644 --- a/spec/controllers/users/carte_controller_shared_example.rb +++ b/spec/controllers/users/carte_controller_shared_example.rb @@ -81,9 +81,9 @@ shared_examples 'carte_controller_spec' do let(:module_api_carto) { create(:module_api_carto, :with_quartiers_prioritaires) } before do - allow_any_instance_of(CARTO::SGMAP::QuartiersPrioritaires::Adapter). - to receive(:to_params). - and_return({"QPCODE1234" => {:code => "QPCODE1234", :nom => "QP de test", :commune => "Paris", :geometry => {:type => "MultiPolygon", :coordinates => [[[[2.38715792094576, 48.8723062632126], [2.38724851642619, 48.8721392348061]]]]}}}) + allow_any_instance_of(CARTO::SGMAP::QuartiersPrioritaires::Adapter) + .to receive(:to_params) + .and_return({"QPCODE1234" => {:code => "QPCODE1234", :nom => "QP de test", :commune => "Paris", :geometry => {:type => "MultiPolygon", :coordinates => [[[[2.38715792094576, 48.8723062632126], [2.38724851642619, 48.8721392348061]]]]}}}) post :save, params: {dossier_id: dossier.id, json_latlngs: json_latlngs} end @@ -128,9 +128,9 @@ shared_examples 'carte_controller_spec' do let(:module_api_carto) { create(:module_api_carto, :with_cadastre) } before do - allow_any_instance_of(CARTO::SGMAP::Cadastre::Adapter). - to receive(:to_params). - and_return([{:surface_intersection => "0.0006", :surface_parcelle => 11252.692583090324, :numero => "0013", :feuille => 1, :section => "CD", :code_dep => "30", :nom_com => "Le Grau-du-Roi", :code_com => "133", :code_arr => "000", :geometry => {:type => "MultiPolygon", :coordinates => [[[[4.134084, 43.5209193], [4.1346615, 43.5212035], [4.1346984, 43.521189], [4.135096, 43.5213848], [4.1350839, 43.5214122], [4.1352697, 43.521505], [4.1356278, 43.5211065], [4.1357402, 43.5207188], [4.1350935, 43.5203936], [4.135002, 43.5204366], [4.1346051, 43.5202412], [4.134584, 43.5202472], [4.1345572, 43.5202551], [4.134356, 43.5203137], [4.1342488, 43.5203448], [4.134084, 43.5209193]]]]}}]) + allow_any_instance_of(CARTO::SGMAP::Cadastre::Adapter) + .to receive(:to_params) + .and_return([{:surface_intersection => "0.0006", :surface_parcelle => 11252.692583090324, :numero => "0013", :feuille => 1, :section => "CD", :code_dep => "30", :nom_com => "Le Grau-du-Roi", :code_com => "133", :code_arr => "000", :geometry => {:type => "MultiPolygon", :coordinates => [[[[4.134084, 43.5209193], [4.1346615, 43.5212035], [4.1346984, 43.521189], [4.135096, 43.5213848], [4.1350839, 43.5214122], [4.1352697, 43.521505], [4.1356278, 43.5211065], [4.1357402, 43.5207188], [4.1350935, 43.5203936], [4.135002, 43.5204366], [4.1346051, 43.5202412], [4.134584, 43.5202472], [4.1345572, 43.5202551], [4.134356, 43.5203137], [4.1342488, 43.5203448], [4.134084, 43.5209193]]]]}}]) post :save, params: {dossier_id: dossier.id, json_latlngs: json_latlngs} end @@ -244,9 +244,9 @@ shared_examples 'carte_controller_spec' do describe 'POST #get_qp' do before do - allow_any_instance_of(CARTO::SGMAP::QuartiersPrioritaires::Adapter). - to receive(:to_params). - and_return({"QPCODE1234" => {:code => "QPCODE1234", :geometry => {:type => "MultiPolygon", :coordinates => [[[[2.38715792094576, 48.8723062632126], [2.38724851642619, 48.8721392348061]]]]}}}) + allow_any_instance_of(CARTO::SGMAP::QuartiersPrioritaires::Adapter) + .to receive(:to_params) + .and_return({"QPCODE1234" => {:code => "QPCODE1234", :geometry => {:type => "MultiPolygon", :coordinates => [[[[2.38715792094576, 48.8723062632126], [2.38724851642619, 48.8721392348061]]]]}}}) post :get_qp, params: {dossier_id: dossier.id, coordinates: coordinates} end diff --git a/spec/features/admin/connection_spec.rb b/spec/features/admin/connection_spec.rb index 3966641c9..f485b0715 100644 --- a/spec/features/admin/connection_spec.rb +++ b/spec/features/admin/connection_spec.rb @@ -30,8 +30,8 @@ feature 'Administrator connection' do end context 'when clicking on sign-out' do before do - stub_request(:get, "https://api.github.com/repos/sgmap/tps/releases/latest"). - to_return(:status => 200, :body => '{"tag_name": "plip", "body": "blabla", "published_at": "2016-02-09T16:46:47Z"}', :headers => {}) + stub_request(:get, "https://api.github.com/repos/sgmap/tps/releases/latest") + .to_return(:status => 200, :body => '{"tag_name": "plip", "body": "blabla", "published_at": "2016-02-09T16:46:47Z"}', :headers => {}) page.find_by_id('sign-out').find('a.fa-sign-out').click end diff --git a/spec/lib/carto/sgmap/api_spec.rb b/spec/lib/carto/sgmap/api_spec.rb index 1a383a96f..7825b9af7 100644 --- a/spec/lib/carto/sgmap/api_spec.rb +++ b/spec/lib/carto/sgmap/api_spec.rb @@ -5,10 +5,10 @@ describe CARTO::SGMAP::API do subject { described_class.search_qp(geojson) } before do - stub_request(:post, "https://apicarto.sgmap.fr/quartiers-prioritaires/search"). - with(:body => /.*/, - :headers => {'Content-Type'=>'application/json'}). - to_return(status: status, body: body) + stub_request(:post, "https://apicarto.sgmap.fr/quartiers-prioritaires/search") + .with(:body => /.*/, + :headers => {'Content-Type'=>'application/json'}) + .to_return(status: status, body: body) end context 'when geojson is empty' do let(:geojson) { '' } @@ -53,10 +53,10 @@ describe CARTO::SGMAP::API do subject { described_class.search_cadastre(geojson) } before do - stub_request(:post, "https://apicarto.sgmap.fr/cadastre/geometrie"). - with(:body => /.*/, - :headers => {'Content-Type'=>'application/json'}). - to_return(status: status, body: body) + stub_request(:post, "https://apicarto.sgmap.fr/cadastre/geometrie") + .with(:body => /.*/, + :headers => {'Content-Type'=>'application/json'}) + .to_return(status: status, body: body) end context 'when geojson is empty' do let(:geojson) { '' } diff --git a/spec/lib/carto/sgmap/cadastre/adapter_spec.rb b/spec/lib/carto/sgmap/cadastre/adapter_spec.rb index e6912c1cb..96734e784 100644 --- a/spec/lib/carto/sgmap/cadastre/adapter_spec.rb +++ b/spec/lib/carto/sgmap/cadastre/adapter_spec.rb @@ -4,10 +4,10 @@ describe CARTO::SGMAP::Cadastre::Adapter do subject { described_class.new(coordinates).to_params } before do - stub_request(:post, "https://apicarto.sgmap.fr/cadastre/geometrie"). - with(:body => /.*/, - :headers => {'Content-Type' => 'application/json'}). - to_return(status: status, body: body) + stub_request(:post, "https://apicarto.sgmap.fr/cadastre/geometrie") + .with(:body => /.*/, + :headers => {'Content-Type' => 'application/json'}) + .to_return(status: status, body: body) end context 'coordinates are filled' do diff --git a/spec/lib/carto/sgmap/quartiers_prioritaires/adapter_spec.rb b/spec/lib/carto/sgmap/quartiers_prioritaires/adapter_spec.rb index f235dbf51..c72bbc04e 100644 --- a/spec/lib/carto/sgmap/quartiers_prioritaires/adapter_spec.rb +++ b/spec/lib/carto/sgmap/quartiers_prioritaires/adapter_spec.rb @@ -4,10 +4,10 @@ describe CARTO::SGMAP::QuartiersPrioritaires::Adapter do subject { described_class.new(coordinates).to_params } before do - stub_request(:post, "https://apicarto.sgmap.fr/quartiers-prioritaires/search"). - with(:body => /.*/, - :headers => {'Content-Type' => 'application/json'}). - to_return(status: status, body: body) + stub_request(:post, "https://apicarto.sgmap.fr/quartiers-prioritaires/search") + .with(:body => /.*/, + :headers => {'Content-Type' => 'application/json'}) + .to_return(status: status, body: body) end context 'coordinates are filled' do From ff1f679c7bc0321f76e04942f38c7d22ab628aa3 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 15:27:16 +0200 Subject: [PATCH 013/136] Enable the Layout/EmptyLineBetweenDefs cop --- .rubocop.yml | 2 +- app/controllers/admin/gestionnaires_controller.rb | 1 - app/controllers/backoffice/dossiers_controller.rb | 1 - app/services/dossier_service.rb | 1 - db/migrate/20160120094750_create_france_connect_information.rb | 1 - 5 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 3d4951b13..7e78d09be 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -40,7 +40,7 @@ Layout/EmptyLineAfterMagicComment: Enabled: true Layout/EmptyLineBetweenDefs: - Enabled: false + Enabled: true Layout/EmptyLines: Enabled: false diff --git a/app/controllers/admin/gestionnaires_controller.rb b/app/controllers/admin/gestionnaires_controller.rb index e2e5aa343..197489c9c 100644 --- a/app/controllers/admin/gestionnaires_controller.rb +++ b/app/controllers/admin/gestionnaires_controller.rb @@ -11,7 +11,6 @@ class Admin::GestionnairesController < AdminController @gestionnaire ||= Gestionnaire.new end - def create @gestionnaire = Gestionnaire.find_by_email(params[:gestionnaire][:email]) procedure_id = params[:procedure_id] diff --git a/app/controllers/backoffice/dossiers_controller.rb b/app/controllers/backoffice/dossiers_controller.rb index 7014e8c8d..ec3524100 100644 --- a/app/controllers/backoffice/dossiers_controller.rb +++ b/app/controllers/backoffice/dossiers_controller.rb @@ -172,7 +172,6 @@ class Backoffice::DossiersController < Backoffice::DossiersListController redirect_to backoffice_dossiers_path end - def unarchive @dossier = Dossier.find(params[:id]) if @dossier.archived diff --git a/app/services/dossier_service.rb b/app/services/dossier_service.rb index 511e4e1d9..d6bdaddfc 100644 --- a/app/services/dossier_service.rb +++ b/app/services/dossier_service.rb @@ -34,7 +34,6 @@ class DossierService @dossier end - def self.siren siret siret[0..8] end diff --git a/db/migrate/20160120094750_create_france_connect_information.rb b/db/migrate/20160120094750_create_france_connect_information.rb index 8cddaa780..91b47ec10 100644 --- a/db/migrate/20160120094750_create_france_connect_information.rb +++ b/db/migrate/20160120094750_create_france_connect_information.rb @@ -38,7 +38,6 @@ class CreateFranceConnectInformation < ActiveRecord::Migration remove_column :users, :france_connect_particulier_id end - def down add_column :users, :gender, :string add_column :users, :given_name, :string From f496f1adab066675b12be385a142d6f48c7f0598 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 15:33:02 +0200 Subject: [PATCH 014/136] Enable the Layout/EmptyLines cop --- .rubocop.yml | 2 +- Guardfile | 1 - Rakefile | 2 -- app/controllers/backoffice/dossiers_list_controller.rb | 1 - app/services/types_de_champ_service.rb | 1 - config/deploy.rb | 2 -- config/unicorn.rb | 3 --- db/migrate/20150728140340_devise_create_users.rb | 1 - db/migrate/20150731121101_devise_create_pros.rb | 1 - db/migrate/20150918163159_devise_create_gestionnaires.rb | 1 - db/migrate/20150922141232_create_users.rb | 1 - db/migrate/20151023132121_devise_create_administrateurs.rb | 1 - db/migrate/20160223134354_devise_create_administrations.rb | 1 - lib/tasks/cloud_storage.rake | 1 - spec/controllers/admin/procedures_controller_spec.rb | 1 - spec/controllers/backoffice/dossiers_controller_spec.rb | 2 -- spec/controllers/users/sessions_controller_spec.rb | 1 - spec/decorators/champ_decorator_spec.rb | 1 - spec/decorators/type_de_champ_decorator_spec.rb | 1 - spec/decorators/type_de_piece_justificative_decorator_spec.rb | 2 -- spec/support/database_cleaner.rb | 1 - 21 files changed, 1 insertion(+), 27 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 7e78d09be..b7a323e8b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -43,7 +43,7 @@ Layout/EmptyLineBetweenDefs: Enabled: true Layout/EmptyLines: - Enabled: false + Enabled: true Layout/EmptyLinesAroundAccessModifier: Enabled: false diff --git a/Guardfile b/Guardfile index 5a91521bb..ed798d43e 100644 --- a/Guardfile +++ b/Guardfile @@ -24,7 +24,6 @@ # * zeus: 'zeus rspec' (requires the server to be started separetly) # * 'just' rspec: 'rspec' - guard 'livereload' do extensions = { css: :css, diff --git a/Rakefile b/Rakefile index 7cde3d716..bdb67ab62 100644 --- a/Rakefile +++ b/Rakefile @@ -19,7 +19,6 @@ task :deploy_ha do end end - task :deploy_old do domains = %w(37.187.154.237 37.187.249.111) domains.each do |domain| @@ -27,7 +26,6 @@ task :deploy_old do end end - task :deploy_test do domains = %w(192.168.0.116) branch = 'clamav' diff --git a/app/controllers/backoffice/dossiers_list_controller.rb b/app/controllers/backoffice/dossiers_list_controller.rb index a61c0caf6..be3294fab 100644 --- a/app/controllers/backoffice/dossiers_list_controller.rb +++ b/app/controllers/backoffice/dossiers_list_controller.rb @@ -31,7 +31,6 @@ class Backoffice::DossiersListController < ApplicationController dossiers_list_facade liste service = dossiers_list_facade.service - if param_page.nil? params[:dossiers_smart_listing] = {page: dossiers_list_facade.service.default_page} end diff --git a/app/services/types_de_champ_service.rb b/app/services/types_de_champ_service.rb index eba9a549a..2d25d0d6e 100644 --- a/app/services/types_de_champ_service.rb +++ b/app/services/types_de_champ_service.rb @@ -7,7 +7,6 @@ class TypesDeChampService .permit("#{attributes}" => [:libelle, :description, :order_place, :type_champ, :id, :mandatory, :type, drop_down_list_attributes: [:value, :id]]) - parameters[attributes].each do |param_first, param_second| if param_second[:libelle].empty? parameters[attributes].delete(param_first.to_s) diff --git a/config/deploy.rb b/config/deploy.rb index 1895843ba..568cf4771 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -15,7 +15,6 @@ raise "Bad to=#{+ENV['to']}" unless ["staging", "production"].include?(ENV['to'] raise "missing domain, run with 'rake deploy domain=37.187.154.237'" if ENV['domain'].nil? - # set :domain, '5.135.190.60' set :domain, ENV['domain'] set :repository, 'https://github.com/sgmap/tps.git' @@ -72,7 +71,6 @@ set :shared_paths, [ 'app/views/cgu/index.html.haml' ] - set :rbenv_path, "/usr/local/rbenv/bin/rbenv" # Optional settings: diff --git a/config/unicorn.rb b/config/unicorn.rb index 88a5d746a..886a35f25 100644 --- a/config/unicorn.rb +++ b/config/unicorn.rb @@ -8,8 +8,6 @@ # See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete # documentation. - - # Use at least one worker per core if you're on a dedicated server, # more will usually help for _short_ waits on databases/caches. worker_processes 2 @@ -52,7 +50,6 @@ check_client_connection false # local variable to guard against running a hook multiple times run_once = true - before_fork do |server, worker| # the following is highly recomended for Rails + "preload_app true" # as there's no need for the master process to hold a connection diff --git a/db/migrate/20150728140340_devise_create_users.rb b/db/migrate/20150728140340_devise_create_users.rb index 2188a87ae..7fd2ccc33 100644 --- a/db/migrate/20150728140340_devise_create_users.rb +++ b/db/migrate/20150728140340_devise_create_users.rb @@ -30,7 +30,6 @@ class DeviseCreateUsers < ActiveRecord::Migration # t.string :unlock_token # Only if unlock strategy is :email or :both # t.datetime :locked_at - t.timestamps end diff --git a/db/migrate/20150731121101_devise_create_pros.rb b/db/migrate/20150731121101_devise_create_pros.rb index 0b3243ca1..ff1dca742 100644 --- a/db/migrate/20150731121101_devise_create_pros.rb +++ b/db/migrate/20150731121101_devise_create_pros.rb @@ -30,7 +30,6 @@ class DeviseCreatePros < ActiveRecord::Migration # t.string :unlock_token # Only if unlock strategy is :email or :both # t.datetime :locked_at - t.timestamps end diff --git a/db/migrate/20150918163159_devise_create_gestionnaires.rb b/db/migrate/20150918163159_devise_create_gestionnaires.rb index e1640f6c4..218e5084a 100644 --- a/db/migrate/20150918163159_devise_create_gestionnaires.rb +++ b/db/migrate/20150918163159_devise_create_gestionnaires.rb @@ -30,7 +30,6 @@ class DeviseCreateGestionnaires < ActiveRecord::Migration # t.string :unlock_token # Only if unlock strategy is :email or :both # t.datetime :locked_at - t.timestamps end diff --git a/db/migrate/20150922141232_create_users.rb b/db/migrate/20150922141232_create_users.rb index 1b2d78a3c..3fd611f64 100644 --- a/db/migrate/20150922141232_create_users.rb +++ b/db/migrate/20150922141232_create_users.rb @@ -30,7 +30,6 @@ class CreateUsers < ActiveRecord::Migration # t.string :unlock_token # Only if unlock strategy is :email or :both # t.datetime :locked_at - t.timestamps end diff --git a/db/migrate/20151023132121_devise_create_administrateurs.rb b/db/migrate/20151023132121_devise_create_administrateurs.rb index bf5bb9167..34d627e9a 100644 --- a/db/migrate/20151023132121_devise_create_administrateurs.rb +++ b/db/migrate/20151023132121_devise_create_administrateurs.rb @@ -30,7 +30,6 @@ class DeviseCreateAdministrateurs < ActiveRecord::Migration # t.string :unlock_token # Only if unlock strategy is :email or :both # t.datetime :locked_at - t.timestamps end diff --git a/db/migrate/20160223134354_devise_create_administrations.rb b/db/migrate/20160223134354_devise_create_administrations.rb index 8f5460074..7c48f4fcf 100644 --- a/db/migrate/20160223134354_devise_create_administrations.rb +++ b/db/migrate/20160223134354_devise_create_administrations.rb @@ -30,7 +30,6 @@ class DeviseCreateAdministrations < ActiveRecord::Migration # t.string :unlock_token # Only if unlock strategy is :email or :both # t.datetime :locked_at - t.timestamps end diff --git a/lib/tasks/cloud_storage.rake b/lib/tasks/cloud_storage.rake index 92789fa65..d519d401b 100644 --- a/lib/tasks/cloud_storage.rake +++ b/lib/tasks/cloud_storage.rake @@ -91,7 +91,6 @@ namespace :cloudstorage do } end - desc 'Clear old documents in tenant' task :clear do Rake::Task['cloudstorage:init'].invoke diff --git a/spec/controllers/admin/procedures_controller_spec.rb b/spec/controllers/admin/procedures_controller_spec.rb index 0efacba6e..8702ed989 100644 --- a/spec/controllers/admin/procedures_controller_spec.rb +++ b/spec/controllers/admin/procedures_controller_spec.rb @@ -31,7 +31,6 @@ describe Admin::ProceduresController, type: :controller do } } - before do sign_in admin end diff --git a/spec/controllers/backoffice/dossiers_controller_spec.rb b/spec/controllers/backoffice/dossiers_controller_spec.rb index a5889ec9f..6a81f7f4c 100644 --- a/spec/controllers/backoffice/dossiers_controller_spec.rb +++ b/spec/controllers/backoffice/dossiers_controller_spec.rb @@ -181,7 +181,6 @@ describe Backoffice::DossiersController, type: :controller do end end - describe 'POST #search' do describe 'by id' do context 'when I am logged as a gestionnaire' do @@ -284,7 +283,6 @@ describe Backoffice::DossiersController, type: :controller do end subject { post :without_continuation, params: {dossier_id: dossier_id} } - it 'change state to without_continuation' do subject diff --git a/spec/controllers/users/sessions_controller_spec.rb b/spec/controllers/users/sessions_controller_spec.rb index f3e0072b8..b4e916edb 100644 --- a/spec/controllers/users/sessions_controller_spec.rb +++ b/spec/controllers/users/sessions_controller_spec.rb @@ -136,7 +136,6 @@ describe Users::SessionsController, type: :controller do expect(user.loged_in_with_france_connect?).to be_falsey end - context 'when user is connect with france connect particulier' do let(:loged_in_with_france_connect) { 'particulier' } diff --git a/spec/decorators/champ_decorator_spec.rb b/spec/decorators/champ_decorator_spec.rb index a5a2554a4..c0817e4b6 100644 --- a/spec/decorators/champ_decorator_spec.rb +++ b/spec/decorators/champ_decorator_spec.rb @@ -4,7 +4,6 @@ describe ChampDecorator do let(:champ) {create :champ, type_de_champ: (create :type_de_champ_public, type_champ: type_champ)} let(:decorator) { champ.decorate } - describe 'value' do subject { decorator.value } diff --git a/spec/decorators/type_de_champ_decorator_spec.rb b/spec/decorators/type_de_champ_decorator_spec.rb index 88779c782..223ca6cf3 100644 --- a/spec/decorators/type_de_champ_decorator_spec.rb +++ b/spec/decorators/type_de_champ_decorator_spec.rb @@ -51,5 +51,4 @@ describe TypeDeChampDecorator do end end - end diff --git a/spec/decorators/type_de_piece_justificative_decorator_spec.rb b/spec/decorators/type_de_piece_justificative_decorator_spec.rb index f9b9d3a61..cbe3d6704 100644 --- a/spec/decorators/type_de_piece_justificative_decorator_spec.rb +++ b/spec/decorators/type_de_piece_justificative_decorator_spec.rb @@ -49,6 +49,4 @@ describe TypeDePieceJustificativeDecorator do end end end - - end diff --git a/spec/support/database_cleaner.rb b/spec/support/database_cleaner.rb index 96483dbb3..9bbfc5c99 100644 --- a/spec/support/database_cleaner.rb +++ b/spec/support/database_cleaner.rb @@ -14,7 +14,6 @@ RSpec.configure do |config| DatabaseCleaner.strategy = :truncation, { except: expect_list } end - config.before(:each) do DatabaseCleaner.start end From aac75106efcec7e6475499ed6eabc43112a29170 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 15:35:21 +0200 Subject: [PATCH 015/136] Enable the Layout/EmptyLinesAroundAccessModifier cop --- .rubocop.yml | 2 +- app/validators/siret_format_validator.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index b7a323e8b..7eb773e35 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -46,7 +46,7 @@ Layout/EmptyLines: Enabled: true Layout/EmptyLinesAroundAccessModifier: - Enabled: false + Enabled: true Layout/EmptyLinesAroundBeginBody: Enabled: true diff --git a/app/validators/siret_format_validator.rb b/app/validators/siret_format_validator.rb index 50197f0c6..b891766f1 100644 --- a/app/validators/siret_format_validator.rb +++ b/app/validators/siret_format_validator.rb @@ -9,6 +9,7 @@ class SiretFormatValidator < ActiveModel::EachValidator end private + def luhn_checksum(value) accum = 0 value.reverse.each_char.map(&:to_i).each_with_index do |digit, index| From f1907f4d0eb3d0613af7d35c4194b7a9d44d4794 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 15:58:15 +0200 Subject: [PATCH 016/136] Enable the Layout/EmptyLinesAroundBlockBody cop --- .rubocop.yml | 2 +- app/lib/siade/rna_adapter.rb | 1 - app/models/gestionnaire.rb | 1 - config/initializers/apipie.rb | 1 - config/routes.rb | 2 -- db/migrate/20150623122513_create_entreprise.rb | 1 - .../20160802113112_build_default_preference_list_dossier.rb | 1 - .../20161007095443_reset_all_preference_list_dossier.rb | 1 - db/migrate/20161221153929_create_notification.rb | 2 -- db/schema.rb | 2 -- lib/tasks/cloud_storage.rake | 2 -- spec/controllers/admin/previsualisations_controller_spec.rb | 1 - spec/controllers/admin/procedures_controller_spec.rb | 5 ----- spec/controllers/administrations_controller_spec.rb | 5 ----- spec/controllers/api/statistiques_spec.rb | 1 - spec/controllers/api/v1/dossiers_controller_spec.rb | 1 - spec/controllers/backoffice/avis_controller_spec.rb | 1 - spec/controllers/backoffice/dossiers_controller_spec.rb | 1 - spec/controllers/ban/search_controller_spec.rb | 1 - spec/controllers/cgu_controller_spec.rb | 1 - spec/controllers/demo_controller_spec.rb | 1 - spec/controllers/invites_controller_spec.rb | 1 - spec/controllers/ping_controller_spec.rb | 2 -- spec/controllers/root_controller_spec.rb | 2 -- spec/controllers/users/carte_controller_shared_example.rb | 1 - .../users/description_controller_shared_example.rb | 1 - spec/controllers/users/dossiers/add_siret_controller_spec.rb | 1 - .../users/dossiers/commentaires_controller_spec.rb | 2 -- spec/controllers/users/dossiers_controller_spec.rb | 3 --- spec/controllers/users/recapitulatif_controller_spec.rb | 1 - spec/controllers/users/registrations_controller_spec.rb | 1 - spec/controllers/users/sessions_controller_spec.rb | 2 -- spec/decorators/procedure_decorator_spec.rb | 1 - spec/decorators/procedures_decorator_spec.rb | 1 - spec/decorators/type_de_champ_decorator_spec.rb | 2 -- .../decorators/type_de_piece_justificative_decorator_spec.rb | 1 - spec/facades/dossiers_list_facades_spec.rb | 2 -- spec/factories/champ.rb | 1 - spec/factories/dossier.rb | 1 - spec/factories/type_de_piece_justificative.rb | 1 - spec/features/admin/add_type_de_champ_spec.rb | 1 - spec/features/admin/connection_spec.rb | 1 - spec/features/admin/procedure_cloning_spec.rb | 2 -- spec/features/admin/procedure_creation_spec.rb | 3 --- spec/features/admin/procedure_locked_spec.rb | 1 - spec/features/backoffice/index_show_procedure_spec.rb | 1 - .../lateral_page_pref_list_dossier_backoffice_spec.rb | 1 - ...al_page_pref_list_dossier_by_procedure_backoffice_spec.rb | 1 - .../france_connect/france_connect_particulier_spec.rb | 2 -- spec/features/users/dossier_edition_spec.rb | 2 -- spec/features/users/dossier_index_spec.rb | 3 --- spec/lib/carto/geo_api/driver_spec.rb | 2 -- spec/mailers/avis_mailer_spec.rb | 1 - spec/models/dossier_spec.rb | 4 ---- spec/models/drop_down_list_spec.rb | 2 -- spec/models/etablissement_spec.rb | 1 - spec/models/gestionnaire_spec.rb | 1 - spec/models/invite_spec.rb | 1 - spec/models/piece_justificative_spec.rb | 1 - spec/models/preference_list_dossier_spec.rb | 1 - spec/models/procedure_path_spec.rb | 1 - spec/models/procedure_spec.rb | 2 -- spec/models/quartier_prioritaire_spec.rb | 1 - spec/models/siret_spec.rb | 1 - spec/models/type_de_champ_shared_example.rb | 1 - spec/models/type_de_champ_spec.rb | 1 - spec/models/user_spec.rb | 1 - spec/serializers/dossier_table_export_serializer_spec.rb | 4 ---- spec/services/accompagnateur_service_spec.rb | 3 --- spec/services/dossiers_list_gestionnaire_service_spec.rb | 5 ----- spec/services/notification_service_spec.rb | 1 - spec/services/previsualisation_service_spec.rb | 1 - spec/services/user_routes_authorization_service_spec.rb | 1 - spec/support/database_cleaner.rb | 1 - spec/uploaders/base_uploader_spec.rb | 1 - spec/uploaders/remote_downloader_spec.rb | 1 - spec/views/admin/previsualisations/show.html.haml_spec.rb | 1 - spec/views/admin/procedures/show.html.haml_spec.rb | 3 --- spec/views/layouts/_navbar_spec.rb | 2 -- .../_left_panel_backoffice_dossierscontroller_show_spec.rb | 4 ---- .../_left_panel_users_recapitulatifcontroller_show_spec.rb | 1 - spec/views/users/description/show.html.haml_spec.rb | 1 - spec/views/users/recapitulatif/show.html.haml_spec.rb | 2 -- spec/workers/auto_archive_procedure_worker_spec.rb | 1 - 84 files changed, 1 insertion(+), 133 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 7eb773e35..e11f5c153 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -52,7 +52,7 @@ Layout/EmptyLinesAroundBeginBody: Enabled: true Layout/EmptyLinesAroundBlockBody: - Enabled: false + Enabled: true Layout/EmptyLinesAroundClassBody: Enabled: false diff --git a/app/lib/siade/rna_adapter.rb b/app/lib/siade/rna_adapter.rb index 85893eff8..80580c336 100644 --- a/app/lib/siade/rna_adapter.rb +++ b/app/lib/siade/rna_adapter.rb @@ -12,7 +12,6 @@ class SIADE::RNAAdapter data_source[:association].each do |k, v| params[k] = v if attr_to_fetch.include?(k) - end params[:association_id] = params[:id] diff --git a/app/models/gestionnaire.rb b/app/models/gestionnaire.rb index 190b9502c..d010bdcf5 100644 --- a/app/models/gestionnaire.rb +++ b/app/models/gestionnaire.rb @@ -55,7 +55,6 @@ class Gestionnaire < ActiveRecord::Base PreferenceListDossier.available_columns_for(procedure_id).each do |table| table.second.each do |column| - if valid_couple_table_attr? table.first, column.first PreferenceListDossier.create( libelle: column.second[:libelle], diff --git a/config/initializers/apipie.rb b/config/initializers/apipie.rb index 92f93c63d..383abc409 100644 --- a/config/initializers/apipie.rb +++ b/config/initializers/apipie.rb @@ -14,5 +14,4 @@ Apipie.configure do |config| Description EOS - end diff --git a/config/routes.rb b/config/routes.rb index 8e2c90217..7aff0071f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,4 @@ Rails.application.routes.draw do - get "/ping" => "ping#index", :constraints => {:ip => /127.0.0.1/} devise_for :administrations, skip: [:password, :registrations] @@ -144,7 +143,6 @@ Rails.application.routes.draw do resource :accompagnateurs, only: [:show, :update] resource :previsualisation, only: [:show] - end namespace :accompagnateurs do diff --git a/db/migrate/20150623122513_create_entreprise.rb b/db/migrate/20150623122513_create_entreprise.rb index 319a3c962..fff62fd73 100644 --- a/db/migrate/20150623122513_create_entreprise.rb +++ b/db/migrate/20150623122513_create_entreprise.rb @@ -13,7 +13,6 @@ class CreateEntreprise < ActiveRecord::Migration t.integer :date_creation t.string :nom t.string :prenom - end add_reference :entreprises, :dossier, references: :dossiers end diff --git a/db/migrate/20160802113112_build_default_preference_list_dossier.rb b/db/migrate/20160802113112_build_default_preference_list_dossier.rb index d5fad79a4..a57a5cbfe 100644 --- a/db/migrate/20160802113112_build_default_preference_list_dossier.rb +++ b/db/migrate/20160802113112_build_default_preference_list_dossier.rb @@ -4,7 +4,6 @@ class BuildDefaultPreferenceListDossier < ActiveRecord::Migration PreferenceListDossier.available_columns.each do |table| table.second.each do |column| - if valid_couple_table_attr? table.first, column.first PreferenceListDossier.create( libelle: column.second[:libelle], diff --git a/db/migrate/20161007095443_reset_all_preference_list_dossier.rb b/db/migrate/20161007095443_reset_all_preference_list_dossier.rb index b137b1d8f..f4594bd5d 100644 --- a/db/migrate/20161007095443_reset_all_preference_list_dossier.rb +++ b/db/migrate/20161007095443_reset_all_preference_list_dossier.rb @@ -99,7 +99,6 @@ class ResetAllPreferenceListDossier < ActiveRecord::Migration PreferenceListDossier.available_columns_for(procedure_id).each do |table| table.second.each do |column| - if valid_couple_table_attr? table.first, column.first PreferenceListDossier.create( libelle: column.second[:libelle], diff --git a/db/migrate/20161221153929_create_notification.rb b/db/migrate/20161221153929_create_notification.rb index 745086412..b6158499e 100644 --- a/db/migrate/20161221153929_create_notification.rb +++ b/db/migrate/20161221153929_create_notification.rb @@ -1,14 +1,12 @@ class CreateNotification < ActiveRecord::Migration[5.0] def change create_table :notifications do |t| - t.boolean :already_read, default: false t.string :liste, array: true t.boolean :multiple, default: false t.string :type_notif t.datetime :created_at t.datetime :updated_at - end add_belongs_to :notifications, :dossier diff --git a/db/schema.rb b/db/schema.rb index 81476287a..0fd7a56a4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,6 @@ # It's strongly recommended that you check this file into your version control system. ActiveRecord::Schema.define(version: 20170523092900) do - # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -475,5 +474,4 @@ ActiveRecord::Schema.define(version: 20170523092900) do LEFT JOIN individuals ON ((individuals.dossier_id = dossiers.id))) LEFT JOIN pieces_justificatives ON ((pieces_justificatives.dossier_id = dossiers.id))); SQL - end diff --git a/lib/tasks/cloud_storage.rake b/lib/tasks/cloud_storage.rake index d519d401b..06b8c836d 100644 --- a/lib/tasks/cloud_storage.rake +++ b/lib/tasks/cloud_storage.rake @@ -1,5 +1,4 @@ namespace :cloudstorage do - task init: :environment do os_config = (YAML.load_file(Fog.credentials_path))['default'] @os = OpenStack::Connection.create( @@ -109,5 +108,4 @@ namespace :cloudstorage do @cont.delete_object(object) unless last_modified.utc > (Time.now - 2.year).utc } end - end diff --git a/spec/controllers/admin/previsualisations_controller_spec.rb b/spec/controllers/admin/previsualisations_controller_spec.rb index 6ca5b8768..6d2adedad 100644 --- a/spec/controllers/admin/previsualisations_controller_spec.rb +++ b/spec/controllers/admin/previsualisations_controller_spec.rb @@ -12,5 +12,4 @@ describe Admin::PrevisualisationsController, type: :controller do subject { get :show, params: {procedure_id: procedure.id} } it { expect(subject.status).to eq(200) } end - end diff --git a/spec/controllers/admin/procedures_controller_spec.rb b/spec/controllers/admin/procedures_controller_spec.rb index 8702ed989..db036feed 100644 --- a/spec/controllers/admin/procedures_controller_spec.rb +++ b/spec/controllers/admin/procedures_controller_spec.rb @@ -54,7 +54,6 @@ describe Admin::ProceduresController, type: :controller do end describe 'DELETE #destroy' do - let(:procedure_draft) { create :procedure, published: false, archived: false } let(:procedure_published) { create :procedure, published: true, archived: false } let(:procedure_archived) { create :procedure, published: false, archived: true } @@ -150,7 +149,6 @@ describe Admin::ProceduresController, type: :controller do it { expect(subject.direction).to eq(direction) } it { expect(subject.lien_demarche).to eq(lien_demarche) } it { expect(subject.administrateur_id).to eq(admin.id) } - end describe 'procedure module api carto attributs in database' do @@ -278,7 +276,6 @@ describe Admin::ProceduresController, type: :controller do it { expect(subject.for_individual).not_to eq procedure_params[:for_individual] } it { expect(subject.individual_with_siret).not_to eq procedure_params[:individual_with_siret] } it { expect(subject.use_api_carto).not_to eq procedure_params[:module_api_carto_attributes][:use_api_carto] } - end end end @@ -465,7 +462,6 @@ describe Admin::ProceduresController, type: :controller do it { expect(body.first['mine']).to be_truthy } it { expect(body.second['label']).to eq(procedure2.path) } it { expect(body.second['mine']).to be_falsy } - end context 'filtered' do @@ -482,7 +478,6 @@ describe Admin::ProceduresController, type: :controller do end context 'when procedure is archived' do - before do procedure3.update_attribute :archived, true subject diff --git a/spec/controllers/administrations_controller_spec.rb b/spec/controllers/administrations_controller_spec.rb index 2481e327b..5ea846d1f 100644 --- a/spec/controllers/administrations_controller_spec.rb +++ b/spec/controllers/administrations_controller_spec.rb @@ -1,11 +1,9 @@ require 'spec_helper' describe AdministrationsController, type: :controller do - let(:administration) { create :administration } describe 'GET #index' do - subject { get :index } context 'when administration user is not connect' do @@ -13,7 +11,6 @@ describe AdministrationsController, type: :controller do end context 'when administration user is connect' do - before do sign_in administration end @@ -23,7 +20,6 @@ describe AdministrationsController, type: :controller do end describe 'POST #create' do - let(:email) { 'plop@plop.com' } let(:password) { 'password' } @@ -43,7 +39,6 @@ describe AdministrationsController, type: :controller do expect(NewAdminMailer).to receive(:deliver_now!) subject end - end context 'when email or password are missing' do diff --git a/spec/controllers/api/statistiques_spec.rb b/spec/controllers/api/statistiques_spec.rb index 0f3a85c39..fa04982ad 100644 --- a/spec/controllers/api/statistiques_spec.rb +++ b/spec/controllers/api/statistiques_spec.rb @@ -2,7 +2,6 @@ require 'spec_helper' describe API::StatistiquesController, type: :controller do describe '#GET dossiers_stats' do - before do get :dossiers_stats end diff --git a/spec/controllers/api/v1/dossiers_controller_spec.rb b/spec/controllers/api/v1/dossiers_controller_spec.rb index bcbf27315..809711108 100644 --- a/spec/controllers/api/v1/dossiers_controller_spec.rb +++ b/spec/controllers/api/v1/dossiers_controller_spec.rb @@ -95,7 +95,6 @@ describe API::V1::DossiersController do end context 'when procedure is found and belongs to current admin' do - context 'when dossier does not exist' do let(:procedure_id) { procedure.id } let(:dossier_id) { 99999 } diff --git a/spec/controllers/backoffice/avis_controller_spec.rb b/spec/controllers/backoffice/avis_controller_spec.rb index c1af9658b..33d635c97 100644 --- a/spec/controllers/backoffice/avis_controller_spec.rb +++ b/spec/controllers/backoffice/avis_controller_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe Backoffice::AvisController, type: :controller do - describe '#POST create' do let(:gestionnaire){ create(:gestionnaire) } let!(:dossier){ create(:dossier, state: 'received') } diff --git a/spec/controllers/backoffice/dossiers_controller_spec.rb b/spec/controllers/backoffice/dossiers_controller_spec.rb index 6a81f7f4c..b21bd6e32 100644 --- a/spec/controllers/backoffice/dossiers_controller_spec.rb +++ b/spec/controllers/backoffice/dossiers_controller_spec.rb @@ -346,7 +346,6 @@ describe Backoffice::DossiersController, type: :controller do it 'change state for updated' do expect(dossier.state).to eq 'updated' end - end describe 'flash alert' do diff --git a/spec/controllers/ban/search_controller_spec.rb b/spec/controllers/ban/search_controller_spec.rb index e51ef2a52..7fe454e19 100644 --- a/spec/controllers/ban/search_controller_spec.rb +++ b/spec/controllers/ban/search_controller_spec.rb @@ -16,7 +16,6 @@ describe Ban::SearchController, type: :controller do end context 'when request return nothing', vcr: {cassette_name: 'bano_search_nothing'} do - let (:request) { 'je recherche pas grand chose' } it { expect(response.status).to eq 200 } diff --git a/spec/controllers/cgu_controller_spec.rb b/spec/controllers/cgu_controller_spec.rb index c656c0ca1..710f6ddb2 100644 --- a/spec/controllers/cgu_controller_spec.rb +++ b/spec/controllers/cgu_controller_spec.rb @@ -2,7 +2,6 @@ require 'spec_helper' describe CguController, type: :controller do describe 'GET #index' do - subject { get :index } it { expect(subject.status).to eq 200 } diff --git a/spec/controllers/demo_controller_spec.rb b/spec/controllers/demo_controller_spec.rb index 4411a2848..5e9ef92e9 100644 --- a/spec/controllers/demo_controller_spec.rb +++ b/spec/controllers/demo_controller_spec.rb @@ -2,7 +2,6 @@ require 'spec_helper' describe DemoController, type: :controller do describe 'GET #index' do - subject { get :index } it { expect(subject.status).to eq 200 } diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb index 9f6d38d81..9577aca7c 100644 --- a/spec/controllers/invites_controller_spec.rb +++ b/spec/controllers/invites_controller_spec.rb @@ -41,7 +41,6 @@ describe InvitesController, type: :controller do it { expect(invite.user).to eq user } it { expect(flash[:notice]).to be_present } - end context 'when email is not assign to an user' do diff --git a/spec/controllers/ping_controller_spec.rb b/spec/controllers/ping_controller_spec.rb index 91a05ee16..1c723512c 100644 --- a/spec/controllers/ping_controller_spec.rb +++ b/spec/controllers/ping_controller_spec.rb @@ -2,13 +2,11 @@ require 'spec_helper' describe PingController, type: :controller do describe 'GET #index' do - subject { get :index } it { expect(subject.status).to eq 200 } context 'when base is un-plug' do - before do allow(ActiveRecord::Base).to receive(:connected?).and_return(false) end diff --git a/spec/controllers/root_controller_spec.rb b/spec/controllers/root_controller_spec.rb index 3109f46ef..4ea5e837b 100644 --- a/spec/controllers/root_controller_spec.rb +++ b/spec/controllers/root_controller_spec.rb @@ -64,7 +64,6 @@ describe RootController, type: :controller do end it { expect(response.body).to have_css('.landing') } - end context "unified login" do @@ -78,5 +77,4 @@ describe RootController, type: :controller do expect(response.body).to have_css("a[href='#{new_user_session_path}']") end end - end diff --git a/spec/controllers/users/carte_controller_shared_example.rb b/spec/controllers/users/carte_controller_shared_example.rb index 5208fc3e7..dff16acca 100644 --- a/spec/controllers/users/carte_controller_shared_example.rb +++ b/spec/controllers/users/carte_controller_shared_example.rb @@ -179,7 +179,6 @@ shared_examples 'carte_controller_spec' do end describe '#get_position' do - context 'when etablissement is nil' do before do dossier.update etablissement: nil diff --git a/spec/controllers/users/description_controller_shared_example.rb b/spec/controllers/users/description_controller_shared_example.rb index 9a73c5735..3d3a71114 100644 --- a/spec/controllers/users/description_controller_shared_example.rb +++ b/spec/controllers/users/description_controller_shared_example.rb @@ -87,7 +87,6 @@ shared_examples 'description_controller_spec' do context 'when dossier does not have an enterprise datas' do before do - end it { expect(dossier.entreprise).to be_nil } diff --git a/spec/controllers/users/dossiers/add_siret_controller_spec.rb b/spec/controllers/users/dossiers/add_siret_controller_spec.rb index 45962c0fc..2481fd480 100644 --- a/spec/controllers/users/dossiers/add_siret_controller_spec.rb +++ b/spec/controllers/users/dossiers/add_siret_controller_spec.rb @@ -2,7 +2,6 @@ require 'spec_helper' describe Users::Dossiers::AddSiretController, type: :controller do describe '#GET show' do - let(:dossier) { create :dossier } before do diff --git a/spec/controllers/users/dossiers/commentaires_controller_spec.rb b/spec/controllers/users/dossiers/commentaires_controller_spec.rb index f83943979..1fd067919 100644 --- a/spec/controllers/users/dossiers/commentaires_controller_spec.rb +++ b/spec/controllers/users/dossiers/commentaires_controller_spec.rb @@ -5,7 +5,6 @@ describe Users::Dossiers::CommentairesController, type: :controller do let(:texte_commentaire) { 'Commentaire de test' } describe '#POST create' do - subject { post :create, params:{dossier_id: dossier.id, texte_commentaire: texte_commentaire} dossier.reload @@ -42,7 +41,6 @@ describe Users::Dossiers::CommentairesController, type: :controller do expect(NotificationMailer).to_not receive(:new_answer) subject end - end end end diff --git a/spec/controllers/users/dossiers_controller_spec.rb b/spec/controllers/users/dossiers_controller_spec.rb index 458db571f..01c8f79a2 100644 --- a/spec/controllers/users/dossiers_controller_spec.rb +++ b/spec/controllers/users/dossiers_controller_spec.rb @@ -66,7 +66,6 @@ describe Users::DossiersController, type: :controller do it { expect { subject }.to change(Dossier, :count).by 1 } describe 'save user siret' do - context 'when user have not a saved siret' do context 'when siret is present on request' do subject { get :new, params: {procedure_id: procedure_id, siret: siret} } @@ -349,7 +348,6 @@ describe Users::DossiersController, type: :controller do end it { expect(response.status).to eq 200 } - end end @@ -456,7 +454,6 @@ describe Users::DossiersController, type: :controller do it { expect { subject }.to change { Dossier.count }.by(0) } end - end describe 'PUT #change_siret' do diff --git a/spec/controllers/users/recapitulatif_controller_spec.rb b/spec/controllers/users/recapitulatif_controller_spec.rb index 06b63f1ea..120e2e344 100644 --- a/spec/controllers/users/recapitulatif_controller_spec.rb +++ b/spec/controllers/users/recapitulatif_controller_spec.rb @@ -33,7 +33,6 @@ describe Users::RecapitulatifController, type: :controller do it { is_expected.to redirect_to root_path } end end - end describe 'POST #initiate' do diff --git a/spec/controllers/users/registrations_controller_spec.rb b/spec/controllers/users/registrations_controller_spec.rb index e65f10a98..324995ca3 100644 --- a/spec/controllers/users/registrations_controller_spec.rb +++ b/spec/controllers/users/registrations_controller_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe Users::RegistrationsController, type: :controller do - let(:email) { 'test@octo.com' } let(:password) { 'password' } diff --git a/spec/controllers/users/sessions_controller_spec.rb b/spec/controllers/users/sessions_controller_spec.rb index b4e916edb..cc0570193 100644 --- a/spec/controllers/users/sessions_controller_spec.rb +++ b/spec/controllers/users/sessions_controller_spec.rb @@ -83,7 +83,6 @@ describe Users::SessionsController, type: :controller do end it 'signs user + gestionnaire + administrateur in' do - post :create, params: {user: {email: administrateur.email, password: administrateur.password}} expect(@response.redirect?).to be(true) expect(subject.current_user).to eq(user) @@ -200,7 +199,6 @@ describe Users::SessionsController, type: :controller do expect(subject.current_administrateur).to be(nil) end end - end end diff --git a/spec/decorators/procedure_decorator_spec.rb b/spec/decorators/procedure_decorator_spec.rb index 26540d80f..4c61964f5 100644 --- a/spec/decorators/procedure_decorator_spec.rb +++ b/spec/decorators/procedure_decorator_spec.rb @@ -25,5 +25,4 @@ describe ProcedureDecorator do it { expect(subject.quartiers_prioritaires).to be_falsey } it { expect(subject.cadastre).to be_falsey } end - end diff --git a/spec/decorators/procedures_decorator_spec.rb b/spec/decorators/procedures_decorator_spec.rb index e298b1180..1ad516042 100644 --- a/spec/decorators/procedures_decorator_spec.rb +++ b/spec/decorators/procedures_decorator_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe ProceduresDecorator do - before do create(:procedure, :published, created_at: Time.new(2015, 12, 24, 14, 10)) create(:procedure, :published, created_at: Time.new(2015, 12, 24, 14, 10)) diff --git a/spec/decorators/type_de_champ_decorator_spec.rb b/spec/decorators/type_de_champ_decorator_spec.rb index 223ca6cf3..0a6ac784a 100644 --- a/spec/decorators/type_de_champ_decorator_spec.rb +++ b/spec/decorators/type_de_champ_decorator_spec.rb @@ -9,7 +9,6 @@ describe TypeDeChampDecorator do let!(:type_de_champ_2) { create(:type_de_champ_public, procedure: procedure, order_place: 2) } describe '#button_up' do - describe 'with first piece justificative' do let(:index) { 0 } subject { type_de_champ_0.decorate } @@ -50,5 +49,4 @@ describe TypeDeChampDecorator do end end end - end diff --git a/spec/decorators/type_de_piece_justificative_decorator_spec.rb b/spec/decorators/type_de_piece_justificative_decorator_spec.rb index cbe3d6704..5503b68a0 100644 --- a/spec/decorators/type_de_piece_justificative_decorator_spec.rb +++ b/spec/decorators/type_de_piece_justificative_decorator_spec.rb @@ -9,7 +9,6 @@ describe TypeDePieceJustificativeDecorator do let!(:type_de_piece_justificative_2) { create(:type_de_piece_justificative, procedure: procedure, order_place: 2) } describe '#button_up' do - describe 'with first piece justificative' do let(:index) { 0 } subject { type_de_piece_justificative_0.decorate } diff --git a/spec/facades/dossiers_list_facades_spec.rb b/spec/facades/dossiers_list_facades_spec.rb index 0a9c7a0d3..08ca0c878 100644 --- a/spec/facades/dossiers_list_facades_spec.rb +++ b/spec/facades/dossiers_list_facades_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe DossiersListFacades do - let(:gestionnaire) { create :gestionnaire } let(:procedure) { create :procedure, libelle: 'Ma procédure' } let(:procedure_2) { create :procedure, libelle: 'Ma seconde procédure' } @@ -27,7 +26,6 @@ describe DossiersListFacades do end describe '#preference_list_dossiers_filter' do - subject { facade.preference_list_dossiers_filter } context 'when procedure is not pasted at the facade' do diff --git a/spec/factories/champ.rb b/spec/factories/champ.rb index 3a2302f4c..630e787ba 100644 --- a/spec/factories/champ.rb +++ b/spec/factories/champ.rb @@ -1,6 +1,5 @@ FactoryGirl.define do factory :champ do - type_de_champ { FactoryGirl.create(:type_de_champ_public) } end end diff --git a/spec/factories/dossier.rb b/spec/factories/dossier.rb index 415e1d58d..f53eeb0f6 100644 --- a/spec/factories/dossier.rb +++ b/spec/factories/dossier.rb @@ -42,7 +42,6 @@ FactoryGirl.define do trait :with_cerfa_upload do after(:build) do |dossier, _evaluator| - dossier.cerfa << create(:cerfa) end end diff --git a/spec/factories/type_de_piece_justificative.rb b/spec/factories/type_de_piece_justificative.rb index 36084ab39..76af4cf37 100644 --- a/spec/factories/type_de_piece_justificative.rb +++ b/spec/factories/type_de_piece_justificative.rb @@ -1,6 +1,5 @@ FactoryGirl.define do factory :type_de_piece_justificative do - libelle 'RIB' description 'Releve identité bancaire' diff --git a/spec/features/admin/add_type_de_champ_spec.rb b/spec/features/admin/add_type_de_champ_spec.rb index adbf661f9..77e0f446b 100644 --- a/spec/features/admin/add_type_de_champ_spec.rb +++ b/spec/features/admin/add_type_de_champ_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' feature 'add a new type de champs', js: true do - let(:administrateur) { create(:administrateur) } let(:procedure) { create(:procedure, administrateur: administrateur) } diff --git a/spec/features/admin/connection_spec.rb b/spec/features/admin/connection_spec.rb index f485b0715..4eb5236e4 100644 --- a/spec/features/admin/connection_spec.rb +++ b/spec/features/admin/connection_spec.rb @@ -58,6 +58,5 @@ feature 'Administrator connection' do end end end - end end diff --git a/spec/features/admin/procedure_cloning_spec.rb b/spec/features/admin/procedure_cloning_spec.rb index 78e13045a..15530b161 100644 --- a/spec/features/admin/procedure_cloning_spec.rb +++ b/spec/features/admin/procedure_cloning_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' feature 'As an administrateur I wanna clone a procedure', js: true do - let(:administrateur) { create(:administrateur) } before do @@ -10,7 +9,6 @@ feature 'As an administrateur I wanna clone a procedure', js: true do end context 'Cloning procedure' do - before 'Create procedure' do page.find_by_id('new-procedure').click fill_in 'procedure_libelle', with: 'libelle de la procedure' diff --git a/spec/features/admin/procedure_creation_spec.rb b/spec/features/admin/procedure_creation_spec.rb index 8e14bd43a..987030e7c 100644 --- a/spec/features/admin/procedure_creation_spec.rb +++ b/spec/features/admin/procedure_creation_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' feature 'As an administrateur I wanna create a new procedure', js: true do - let(:administrateur) { create(:administrateur) } before do @@ -10,7 +9,6 @@ feature 'As an administrateur I wanna create a new procedure', js: true do end context 'Right after sign_in I shall see all procedure states links' do - scenario 'Finding draft procedures' do page.find_by_id('draft-procedures').trigger('click') expect(page).to have_current_path(admin_procedures_draft_path, only_path: true) @@ -28,7 +26,6 @@ feature 'As an administrateur I wanna create a new procedure', js: true do end context 'Creating a new procedure' do - scenario 'Finding new procedure link' do page.find_by_id('new-procedure').click expect(page).to have_current_path(new_admin_procedure_path, only_path: true) diff --git a/spec/features/admin/procedure_locked_spec.rb b/spec/features/admin/procedure_locked_spec.rb index 12961e56c..2a89232b0 100644 --- a/spec/features/admin/procedure_locked_spec.rb +++ b/spec/features/admin/procedure_locked_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' feature 'procedure locked' do - let(:administrateur) { create(:administrateur) } let(:published) { false } let(:procedure) { create(:procedure, administrateur: administrateur, published: published) } diff --git a/spec/features/backoffice/index_show_procedure_spec.rb b/spec/features/backoffice/index_show_procedure_spec.rb index d24910eca..ef0eade1a 100644 --- a/spec/features/backoffice/index_show_procedure_spec.rb +++ b/spec/features/backoffice/index_show_procedure_spec.rb @@ -20,7 +20,6 @@ feature 'As an Accompagnateur I can navigate and use each functionnality around end context 'On index' do - scenario 'Switching between procedures' do page.all('#procedure-list a').first.click expect(page).to have_current_path(backoffice_dossiers_procedure_path(id: procedure_1.id.to_s), only_path: true) diff --git a/spec/features/backoffice/lateral_page_pref_list_dossier_backoffice_spec.rb b/spec/features/backoffice/lateral_page_pref_list_dossier_backoffice_spec.rb index 9a36bf116..9e7ff7289 100644 --- a/spec/features/backoffice/lateral_page_pref_list_dossier_backoffice_spec.rb +++ b/spec/features/backoffice/lateral_page_pref_list_dossier_backoffice_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' feature 'usage of pref list dossier lateral panel', js: true do - let(:administrateur) { create(:administrateur) } let(:gestionnaire) { create(:gestionnaire, administrateurs: [administrateur]) } let(:procedure) { create(:procedure, administrateur: administrateur) } diff --git a/spec/features/backoffice/lateral_page_pref_list_dossier_by_procedure_backoffice_spec.rb b/spec/features/backoffice/lateral_page_pref_list_dossier_by_procedure_backoffice_spec.rb index 9ed134a75..21f5e1801 100644 --- a/spec/features/backoffice/lateral_page_pref_list_dossier_by_procedure_backoffice_spec.rb +++ b/spec/features/backoffice/lateral_page_pref_list_dossier_by_procedure_backoffice_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' feature 'usage of pref list dossier lateral panel by procedure', js: true do - let(:administrateur) { create(:administrateur) } let(:gestionnaire) { create(:gestionnaire, administrateurs: [administrateur]) } let(:procedure) { create(:procedure, :with_type_de_champ, administrateur: administrateur) } diff --git a/spec/features/france_connect/france_connect_particulier_spec.rb b/spec/features/france_connect/france_connect_particulier_spec.rb index 781e35b6c..6889ef454 100644 --- a/spec/features/france_connect/france_connect_particulier_spec.rb +++ b/spec/features/france_connect/france_connect_particulier_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' feature 'France Connect Particulier Connexion' do - let(:code) { 'plop' } let(:given_name) { 'titi' } let(:family_name) { 'toto' } @@ -21,7 +20,6 @@ feature 'France Connect Particulier Connexion' do } context 'when user is on login page' do - before do visit new_user_session_path end diff --git a/spec/features/users/dossier_edition_spec.rb b/spec/features/users/dossier_edition_spec.rb index 5aca700d9..238a7c3d9 100644 --- a/spec/features/users/dossier_edition_spec.rb +++ b/spec/features/users/dossier_edition_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' feature 'As a User I want to edit a dossier I own' do - let(:user) { create(:user) } let(:procedure_for_individual) { create(:procedure, :published, :for_individual, :with_type_de_champ, :with_two_type_de_piece_justificative, :with_dossier_link) } let!(:dossier) { create(:dossier, :with_entreprise, :for_individual, :with_dossier_link, procedure: procedure_for_individual, user: user, autorisation_donnees: true, state: 'initiated') } @@ -12,7 +11,6 @@ feature 'As a User I want to edit a dossier I own' do end context 'After sign_in, I can navigate through dossiers indexes and edit a dossier' do - scenario 'After sign_in, I can see dossiers "à traiter" (default), and other indexes' do expect(page.find('#a_traiter')['class'] ).to eq('active procedure-list-element') page.find_by_id('brouillon').click diff --git a/spec/features/users/dossier_index_spec.rb b/spec/features/users/dossier_index_spec.rb index 913efcf79..dc696a9ad 100644 --- a/spec/features/users/dossier_index_spec.rb +++ b/spec/features/users/dossier_index_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' feature 'As a User I want to sort and paginate dossiers', js: true do - let(:user) { create(:user) } let(:procedure_for_individual) { create(:procedure, :published, :for_individual) } @@ -21,7 +20,6 @@ feature 'As a User I want to sort and paginate dossiers', js: true do end context 'After sign_in, I can see my 51 dossiers on the index' do - scenario 'Using sort' do visit "/users/dossiers?dossiers_smart_listing[sort][id]=asc" expect(page.all(:css, '#dossiers-list tr')[1].text.split(" ").first).to eq(user.dossiers.first.id.to_s) @@ -50,5 +48,4 @@ feature 'As a User I want to sort and paginate dossiers', js: true do expect(page.all(:css, '#dossiers-list tr')[1].text.split(" ").first).to eq((user.dossiers.first.id).to_s) end end - end diff --git a/spec/lib/carto/geo_api/driver_spec.rb b/spec/lib/carto/geo_api/driver_spec.rb index 417bf0ba9..4d13c15ca 100644 --- a/spec/lib/carto/geo_api/driver_spec.rb +++ b/spec/lib/carto/geo_api/driver_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe Carto::GeoAPI::Driver do - describe '.regions', vcr: {cassette_name: 'geoapi_regions'} do subject { described_class.regions } @@ -18,7 +17,6 @@ describe Carto::GeoAPI::Driver do subject { described_class.pays } it { is_expected.to eq File.open('app/lib/carto/geo_api/pays.json').read } - end describe 'departements_url' do diff --git a/spec/mailers/avis_mailer_spec.rb b/spec/mailers/avis_mailer_spec.rb index 83673e899..0d9820d32 100644 --- a/spec/mailers/avis_mailer_spec.rb +++ b/spec/mailers/avis_mailer_spec.rb @@ -9,6 +9,5 @@ RSpec.describe AvisMailer, type: :mailer do it { expect(subject.subject).to eq("Donnez votre avis sur le dossier nº #{avis.dossier.id} (#{avis.dossier.procedure.libelle})") } it { expect(subject.body).to include("Vous avez été invité par #{avis.claimant.email} à donner votre avis sur le dossier nº #{avis.dossier.id} de la procédure : #{avis.dossier.procedure.libelle}.") } it { expect(subject.body).to include(avis.introduction) } - end end diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index ac668ee64..ee164c385 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -262,7 +262,6 @@ describe Dossier do it { is_expected.to eq 'replied' } end - end end @@ -295,7 +294,6 @@ describe Dossier do it { is_expected.to eq('replied') } end - end end @@ -661,7 +659,6 @@ describe Dossier do it { expect(subject.second.type_de_champ.libelle).to eq 'type_2_2' } it { expect(subject.last.type_de_champ.libelle).to eq 'type_2_3' } end - end describe '#ordered_champs_private' do @@ -850,7 +847,6 @@ describe Dossier do it_behaves_like 'dossier is processed', 'without_continuation' end - end describe '.downloadable' do diff --git a/spec/models/drop_down_list_spec.rb b/spec/models/drop_down_list_spec.rb index b065dace9..712e76d59 100644 --- a/spec/models/drop_down_list_spec.rb +++ b/spec/models/drop_down_list_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe DropDownList do - describe 'database columns' do it { is_expected.to have_db_column(:value) } end @@ -24,7 +23,6 @@ Pilotage / Ingénierie context 'when one value is empty' do let(:value) { "Cohésion sociale - Cadre de vie / Urb. Pilotage / Ingénierie " diff --git a/spec/models/etablissement_spec.rb b/spec/models/etablissement_spec.rb index 6097cac9c..802672340 100644 --- a/spec/models/etablissement_spec.rb +++ b/spec/models/etablissement_spec.rb @@ -23,7 +23,6 @@ describe Etablissement do end describe '#geo_adresse' do - let(:etablissement) { create(:etablissement) } subject { etablissement.geo_adresse } diff --git a/spec/models/gestionnaire_spec.rb b/spec/models/gestionnaire_spec.rb index 8b85128d4..dc2679604 100644 --- a/spec/models/gestionnaire_spec.rb +++ b/spec/models/gestionnaire_spec.rb @@ -103,7 +103,6 @@ describe Gestionnaire, type: :model do subject { gestionnaire.follow? dossier.id } context 'when gestionnaire follow a dossier' do - before do create :follow, dossier_id: dossier.id, gestionnaire_id: gestionnaire.id end diff --git a/spec/models/invite_spec.rb b/spec/models/invite_spec.rb index 29b53f41a..e6c637d69 100644 --- a/spec/models/invite_spec.rb +++ b/spec/models/invite_spec.rb @@ -32,7 +32,6 @@ describe Invite do end it { expect{ subject }.to raise_error ActiveRecord::RecordInvalid } - end end end diff --git a/spec/models/piece_justificative_spec.rb b/spec/models/piece_justificative_spec.rb index 70abcb299..62da2cd96 100644 --- a/spec/models/piece_justificative_spec.rb +++ b/spec/models/piece_justificative_spec.rb @@ -13,7 +13,6 @@ describe PieceJustificative do it { is_expected.to belong_to(:type_de_piece_justificative) } it { is_expected.to belong_to(:user) } it { is_expected.to have_one(:commentaire) } - end describe 'validations' do diff --git a/spec/models/preference_list_dossier_spec.rb b/spec/models/preference_list_dossier_spec.rb index cba6265a3..9ddb467f5 100644 --- a/spec/models/preference_list_dossier_spec.rb +++ b/spec/models/preference_list_dossier_spec.rb @@ -190,7 +190,6 @@ describe PreferenceListDossier do it { expect(subject[:order]).to be_nil } it { expect(subject[:filter]).to be_nil } end - end describe 'etablissement' do diff --git a/spec/models/procedure_path_spec.rb b/spec/models/procedure_path_spec.rb index dfb268291..6f4565fd3 100644 --- a/spec/models/procedure_path_spec.rb +++ b/spec/models/procedure_path_spec.rb @@ -46,5 +46,4 @@ describe ProcedurePath do end end end - end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index a9e3f2185..067ca981e 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -175,7 +175,6 @@ describe Procedure do subject.types_de_piece_justificative.zip(procedure.types_de_piece_justificative).each do |stc, ptc| expect(stc).to have_same_attributes_as(ptc) end - end it 'should duplicate existing mail_templates' do @@ -244,7 +243,6 @@ describe Procedure do end describe 'total_dossier' do - let(:procedure) { create :procedure } before do diff --git a/spec/models/quartier_prioritaire_spec.rb b/spec/models/quartier_prioritaire_spec.rb index 30c52cd47..6295ad74e 100644 --- a/spec/models/quartier_prioritaire_spec.rb +++ b/spec/models/quartier_prioritaire_spec.rb @@ -9,7 +9,6 @@ describe QuartierPrioritaire do it { is_expected.to belong_to(:dossier) } describe 'geometry' do - let(:qp) { create :quartier_prioritaire, geometry: qp_geometry } let(:qp_geometry) { File.open('spec/support/files/qp_geometry_value.txt').read } diff --git a/spec/models/siret_spec.rb b/spec/models/siret_spec.rb index 3b81bf51b..c5d197660 100644 --- a/spec/models/siret_spec.rb +++ b/spec/models/siret_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe Siret, type: :model do - let(:valid_siret) { '41816609600051' } let(:invalid_siret) { '111111111' } diff --git a/spec/models/type_de_champ_shared_example.rb b/spec/models/type_de_champ_shared_example.rb index c1b37a8f6..3ed2e0f0c 100644 --- a/spec/models/type_de_champ_shared_example.rb +++ b/spec/models/type_de_champ_shared_example.rb @@ -43,7 +43,6 @@ shared_examples 'type_de_champ_spec' do end describe 'field_for_list?' do - let(:type_de_champ_yes) { create :type_de_champ_public, type_champ: 'text' } let(:type_de_champ_no_1) { create :type_de_champ_public, type_champ: 'textarea' } let(:type_de_champ_no_2) { create :type_de_champ_public, type_champ: 'header_section' } diff --git a/spec/models/type_de_champ_spec.rb b/spec/models/type_de_champ_spec.rb index 702b8d64f..e7eb763f5 100644 --- a/spec/models/type_de_champ_spec.rb +++ b/spec/models/type_de_champ_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe TypeDeChamp do - require 'models/type_de_champ_shared_example' it_should_behave_like "type_de_champ_spec" diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 8cac2f476..552d42bca 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -16,7 +16,6 @@ describe User, type: :model do it { is_expected.to have_db_column(:updated_at) } it { is_expected.to have_db_column(:siret) } it { is_expected.to have_db_column(:loged_in_with_france_connect) } - end describe 'associations' do it { is_expected.to have_many(:dossiers) } diff --git a/spec/serializers/dossier_table_export_serializer_spec.rb b/spec/serializers/dossier_table_export_serializer_spec.rb index 3373ecbed..0dcd1598e 100644 --- a/spec/serializers/dossier_table_export_serializer_spec.rb +++ b/spec/serializers/dossier_table_export_serializer_spec.rb @@ -1,9 +1,7 @@ require 'spec_helper' describe DossierTableExportSerializer do - describe '#emails_accompagnateurs' do - let(:gestionnaire){ create(:gestionnaire) } let(:follow) { create(:follow, gestionnaire: gestionnaire) } @@ -28,7 +26,5 @@ describe DossierTableExportSerializer do it { is_expected.to eq "#{gestionnaire.email} #{gestionnaire2.email}" } end - end - end diff --git a/spec/services/accompagnateur_service_spec.rb b/spec/services/accompagnateur_service_spec.rb index 0d01b9f29..eb01bfd54 100644 --- a/spec/services/accompagnateur_service_spec.rb +++ b/spec/services/accompagnateur_service_spec.rb @@ -1,14 +1,12 @@ require 'spec_helper' describe AccompagnateurService do - let(:procedure) { create :procedure } let(:accompagnateur) { create :gestionnaire } let(:accompagnateur_service) { AccompagnateurService.new accompagnateur, procedure, to} describe '#change_assignement!' do - subject { accompagnateur_service.change_assignement! } context 'when accompagnateur is not assign at the procedure' do @@ -34,7 +32,6 @@ describe AccompagnateurService do end describe '#build_default_column' do - subject { accompagnateur_service.build_default_column } context 'when to is not assign' do diff --git a/spec/services/dossiers_list_gestionnaire_service_spec.rb b/spec/services/dossiers_list_gestionnaire_service_spec.rb index de32a0aa9..3af0796b9 100644 --- a/spec/services/dossiers_list_gestionnaire_service_spec.rb +++ b/spec/services/dossiers_list_gestionnaire_service_spec.rb @@ -24,7 +24,6 @@ describe DossiersListGestionnaireService do end context 'when gestionnaire have default sort' do - before do preference_attr.update_column(:order, 'asc') end @@ -74,7 +73,6 @@ describe DossiersListGestionnaireService do it { expect(select_preference_list_dossier.order).to eq 'desc' } end - end context 'when procedure as already a preference order' do @@ -128,7 +126,6 @@ describe DossiersListGestionnaireService do end describe '#join_filter' do - subject { DossiersListGestionnaireService.new(gestionnaire, liste, nil).joins_filter } it { is_expected.to eq []} @@ -180,7 +177,6 @@ describe DossiersListGestionnaireService do it { is_expected.to eq "CAST(dossiers.id as TEXT) LIKE '%23%' AND CAST(entreprises.raison_sociale as TEXT) LIKE '%plop%'" } context 'when last filter caractere is *' do - before do gestionnaire.preference_list_dossiers .find_by(table: 'entreprise', attr: 'raison_sociale', procedure: nil) @@ -242,7 +238,6 @@ describe DossiersListGestionnaireService do end it { is_expected.to eq "CAST(dossiers.id as TEXT) LIKE '%23%' AND CAST(entreprises.raison_sociale as TEXT) LIKE '%plop%' AND champs.type_de_champ_id = 34 AND CAST(champs.value as TEXT) LIKE '%plop%'" } - end end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 6a6cc4762..6368b3eb7 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe NotificationService do - describe '.notify' do let(:dossier) { create :dossier } let(:service) { described_class.new type_notif, dossier.id } diff --git a/spec/services/previsualisation_service_spec.rb b/spec/services/previsualisation_service_spec.rb index 81f84e5ab..64bd03735 100644 --- a/spec/services/previsualisation_service_spec.rb +++ b/spec/services/previsualisation_service_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe PrevisualisationService do - describe '.destroy_all_champs' do subject { described_class.destroy_all_champs dossier } diff --git a/spec/services/user_routes_authorization_service_spec.rb b/spec/services/user_routes_authorization_service_spec.rb index 231e4685b..4bbb7d825 100644 --- a/spec/services/user_routes_authorization_service_spec.rb +++ b/spec/services/user_routes_authorization_service_spec.rb @@ -43,7 +43,6 @@ describe UserRoutesAuthorizationService do let(:controller) { Users::CarteController } context 'when use_api_carto is false' do - describe 'draft' do let(:state) { 'draft' } it { is_expected.to be_falsey } diff --git a/spec/support/database_cleaner.rb b/spec/support/database_cleaner.rb index 9bbfc5c99..fba37f056 100644 --- a/spec/support/database_cleaner.rb +++ b/spec/support/database_cleaner.rb @@ -1,5 +1,4 @@ RSpec.configure do |config| - expect_list = %w() config.before(:suite) do diff --git a/spec/uploaders/base_uploader_spec.rb b/spec/uploaders/base_uploader_spec.rb index d3c9b1f2e..10b0033b0 100644 --- a/spec/uploaders/base_uploader_spec.rb +++ b/spec/uploaders/base_uploader_spec.rb @@ -4,7 +4,6 @@ describe BaseUploader do let(:uploader) { described_class.new } describe '#cache_dir' do - subject { uploader.cache_dir } context 'when rails env is not production' do diff --git a/spec/uploaders/remote_downloader_spec.rb b/spec/uploaders/remote_downloader_spec.rb index 0f0629ee3..1eee1d6c2 100644 --- a/spec/uploaders/remote_downloader_spec.rb +++ b/spec/uploaders/remote_downloader_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe RemoteDownloader do - let(:filename) { 'file_name.pdf' } subject { described_class.new filename } diff --git a/spec/views/admin/previsualisations/show.html.haml_spec.rb b/spec/views/admin/previsualisations/show.html.haml_spec.rb index ffca23efe..0677ee177 100644 --- a/spec/views/admin/previsualisations/show.html.haml_spec.rb +++ b/spec/views/admin/previsualisations/show.html.haml_spec.rb @@ -127,6 +127,5 @@ describe 'admin/previsualisations/show.html.haml', type: :view do context 'when dossier not have cerfa, piece justificative and demarche link' do it { expect(rendered).not_to have_content 'Documents administratifs' } end - end end diff --git a/spec/views/admin/procedures/show.html.haml_spec.rb b/spec/views/admin/procedures/show.html.haml_spec.rb index d974cac37..714b5151d 100644 --- a/spec/views/admin/procedures/show.html.haml_spec.rb +++ b/spec/views/admin/procedures/show.html.haml_spec.rb @@ -23,7 +23,6 @@ describe 'admin/procedures/show.html.haml', type: :view do end context 'when procedure have a gestionnare affected' do - before do create :assign_to, gestionnaire: create(:gestionnaire), procedure: procedure render @@ -76,7 +75,5 @@ describe 'admin/procedures/show.html.haml', type: :view do describe 'procedure link is present' do it { expect(rendered).to have_content(commencer_url(procedure_path: procedure.path)) } end - end - end diff --git a/spec/views/layouts/_navbar_spec.rb b/spec/views/layouts/_navbar_spec.rb index a23f67cae..448456d4f 100644 --- a/spec/views/layouts/_navbar_spec.rb +++ b/spec/views/layouts/_navbar_spec.rb @@ -7,7 +7,6 @@ describe 'layouts/_navbar.html.haml', type: :view do let!(:procedure) { create(:procedure, administrateur: administrateur) } describe 'navbar entries' do - context 'when disconnected' do before do render @@ -39,6 +38,5 @@ describe 'layouts/_navbar.html.haml', type: :view do subject { rendered } it { is_expected.to match(/Déconnexion/) } end - end end diff --git a/spec/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show_spec.rb b/spec/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show_spec.rb index 308acb09c..cd91a032c 100644 --- a/spec/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show_spec.rb +++ b/spec/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe 'layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.html.haml', type: :view do - let!(:dossier) { create(:dossier, :with_entreprise, state: state, archived: archived) } let(:state) { 'draft' } let(:archived) { false } @@ -25,7 +24,6 @@ describe 'layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.htm end context 'button dossier state changements' do - shared_examples 'button Passer en instruction is present' do it { expect(rendered).to have_link('Passer en instruction') } end @@ -149,8 +147,6 @@ describe 'layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.htm it { expect(rendered).to have_content('Archivé') } it { expect(rendered).to have_link('Désarchiver') } - end end - end diff --git a/spec/views/layouts/left_panels/_left_panel_users_recapitulatifcontroller_show_spec.rb b/spec/views/layouts/left_panels/_left_panel_users_recapitulatifcontroller_show_spec.rb index 7996e3d1f..f580715a9 100644 --- a/spec/views/layouts/left_panels/_left_panel_users_recapitulatifcontroller_show_spec.rb +++ b/spec/views/layouts/left_panels/_left_panel_users_recapitulatifcontroller_show_spec.rb @@ -82,5 +82,4 @@ describe 'layouts/left_panels/_left_panel_users_recapitulatifcontroller_show.htm end end end - end diff --git a/spec/views/users/description/show.html.haml_spec.rb b/spec/views/users/description/show.html.haml_spec.rb index c278c09f1..1e35f5d5b 100644 --- a/spec/views/users/description/show.html.haml_spec.rb +++ b/spec/views/users/description/show.html.haml_spec.rb @@ -124,6 +124,5 @@ describe 'users/description/show.html.haml', type: :view do context 'when dossier not have cerfa, piece justificative and demarche link' do it { expect(rendered).not_to have_content 'Documents administratifs' } end - end end diff --git a/spec/views/users/recapitulatif/show.html.haml_spec.rb b/spec/views/users/recapitulatif/show.html.haml_spec.rb index ccef84120..7761bca13 100644 --- a/spec/views/users/recapitulatif/show.html.haml_spec.rb +++ b/spec/views/users/recapitulatif/show.html.haml_spec.rb @@ -69,11 +69,9 @@ describe 'users/recapitulatif/show.html.haml', type: :view do expect(rendered).to have_content('Modifier les documents') expect(rendered).to have_css('#upload-pj-modal') end - end context 'when invite is logged' do - context 'when invite is by Gestionnaire' do let!(:invite_user) { create(:user, email: 'invite@octo.com') } diff --git a/spec/workers/auto_archive_procedure_worker_spec.rb b/spec/workers/auto_archive_procedure_worker_spec.rb index 2a30fa764..6e0743477 100644 --- a/spec/workers/auto_archive_procedure_worker_spec.rb +++ b/spec/workers/auto_archive_procedure_worker_spec.rb @@ -9,7 +9,6 @@ RSpec.describe AutoArchiveProcedureWorker, type: :worker do subject { AutoArchiveProcedureWorker.new.perform } context "when procedures have no auto_archive_on" do - before do subject procedure.reload From 24e7537c49d9926faede4dbe2afbef5a982424dd Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 16:01:04 +0200 Subject: [PATCH 017/136] Enable the Layout/EmptyLinesAroundClassBody cop --- .rubocop.yml | 2 +- app/controllers/api/v1/procedures_controller.rb | 1 - app/controllers/backoffice/avis_controller.rb | 1 - app/controllers/backoffice/dossiers/procedure_controller.rb | 1 - app/decorators/dossiers_decorator.rb | 1 - app/decorators/etablissement_decorator.rb | 1 - app/decorators/procedure_decorator.rb | 1 - app/decorators/procedures_decorator.rb | 1 - app/decorators/type_de_champ_decorator.rb | 1 - app/decorators/type_de_champ_private_decorator.rb | 1 - app/facades/dossier_facades.rb | 1 - app/facades/dossiers_list_facades.rb | 1 - app/facades/invite_dossier_facades.rb | 1 - app/lib/carto/geo_api/driver.rb | 2 -- app/mailers/avis_mailer.rb | 2 -- app/mailers/gestionnaire_mailer.rb | 1 - app/mailers/invite_mailer.rb | 1 - app/models/champ_private.rb | 1 - app/models/champ_public.rb | 1 - app/models/invite.rb | 1 - app/models/invite_gestionnaire.rb | 1 - app/models/invite_user.rb | 1 - app/models/type_de_champ_private.rb | 1 - app/models/type_de_champ_public.rb | 1 - app/services/browser_service.rb | 2 -- app/services/dossier_service.rb | 1 - app/services/dossiers_list_gestionnaire_service.rb | 1 - app/services/france_connect_salt_service.rb | 1 - app/services/notification_service.rb | 1 - app/services/render_partial_service.rb | 1 - app/services/sync_credentials_service.rb | 1 - app/services/user_routes_authorization_service.rb | 1 - app/uploaders/procedure_logo_uploader.rb | 2 -- app/validators/email_format_validator.rb | 1 - app/validators/procedure_path_format_validator.rb | 2 -- config/initializers/stringupcasepatch.rb | 2 -- db/migrate/20151207140202_update_module_api_carto.rb | 3 --- .../20160120094750_create_france_connect_information.rb | 3 --- db/migrate/20160317144949_piece_justificative_have_user.rb | 1 - db/migrate/20160317153115_cerfa_have_user.rb | 1 - .../20160524093540_set_original_filename_for_uploaders.rb | 2 -- .../20160609145737_delete_default_description_to_dossier.rb | 4 ---- ...20160622081321_convert_all_datetime_to_date_on_database.rb | 2 -- .../20160803081304_fix_default_type_on_type_de_champ_table.rb | 1 - db/migrate/20160804130638_add_type_attr_in_champ_table.rb | 1 - db/migrate/20160822142045_delete_value_of_filter_procedure.rb | 1 - ...0160901082824_initiated_all_received_mail_for_procedure.rb | 1 - ...248_change_emptyto_nil_in_preference_list_dossier_table.rb | 1 - ...42_fix_type_de_champ_header_section_with_mandatory_true.rb | 1 - .../20161007095443_reset_all_preference_list_dossier.rb | 1 - spec/mailers/previews/avis_mailer_preview.rb | 2 -- spec/mailers/previews/notification_mailer_preview.rb | 2 -- 52 files changed, 1 insertion(+), 69 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index e11f5c153..3a537645c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -55,7 +55,7 @@ Layout/EmptyLinesAroundBlockBody: Enabled: true Layout/EmptyLinesAroundClassBody: - Enabled: false + Enabled: true Layout/EmptyLinesAroundExceptionHandlingKeywords: Enabled: false diff --git a/app/controllers/api/v1/procedures_controller.rb b/app/controllers/api/v1/procedures_controller.rb index cf370a8ff..74895e845 100644 --- a/app/controllers/api/v1/procedures_controller.rb +++ b/app/controllers/api/v1/procedures_controller.rb @@ -13,5 +13,4 @@ class API::V1::ProceduresController < APIController Rails.logger.error(e.message) render json: {}, status: 404 end - end diff --git a/app/controllers/backoffice/avis_controller.rb b/app/controllers/backoffice/avis_controller.rb index 4d77496d2..5271626c7 100644 --- a/app/controllers/backoffice/avis_controller.rb +++ b/app/controllers/backoffice/avis_controller.rb @@ -1,5 +1,4 @@ class Backoffice::AvisController < ApplicationController - before_action :authenticate_gestionnaire!, except: [:sign_up, :create_gestionnaire] before_action :redirect_if_no_sign_up_needed, only: [:sign_up] before_action :check_avis_exists_and_email_belongs_to_avis, only: [:sign_up, :create_gestionnaire] diff --git a/app/controllers/backoffice/dossiers/procedure_controller.rb b/app/controllers/backoffice/dossiers/procedure_controller.rb index 5f96c1508..00ccbb3b5 100644 --- a/app/controllers/backoffice/dossiers/procedure_controller.rb +++ b/app/controllers/backoffice/dossiers/procedure_controller.rb @@ -21,5 +21,4 @@ class Backoffice::Dossiers::ProcedureController < Backoffice::DossiersListContro def retrieve_procedure current_gestionnaire.procedures.find params[:id] end - end diff --git a/app/decorators/dossiers_decorator.rb b/app/decorators/dossiers_decorator.rb index 6e8571b6f..159bb6aa9 100644 --- a/app/decorators/dossiers_decorator.rb +++ b/app/decorators/dossiers_decorator.rb @@ -1,4 +1,3 @@ class DossiersDecorator < Draper::CollectionDecorator delegate :current_page, :per_page, :offset, :total_entries, :total_pages - end diff --git a/app/decorators/etablissement_decorator.rb b/app/decorators/etablissement_decorator.rb index 23f809c79..b6e03db6a 100644 --- a/app/decorators/etablissement_decorator.rb +++ b/app/decorators/etablissement_decorator.rb @@ -1,4 +1,3 @@ class EtablissementDecorator < Draper::Decorator delegate_all - end diff --git a/app/decorators/procedure_decorator.rb b/app/decorators/procedure_decorator.rb index 561e6754b..50f64c2ab 100644 --- a/app/decorators/procedure_decorator.rb +++ b/app/decorators/procedure_decorator.rb @@ -1,5 +1,4 @@ class ProcedureDecorator < Draper::Decorator - delegate_all def lien diff --git a/app/decorators/procedures_decorator.rb b/app/decorators/procedures_decorator.rb index 380965078..7059df10d 100644 --- a/app/decorators/procedures_decorator.rb +++ b/app/decorators/procedures_decorator.rb @@ -1,4 +1,3 @@ class ProceduresDecorator < Draper::CollectionDecorator delegate :current_page, :per_page, :offset, :total_entries, :total_pages - end diff --git a/app/decorators/type_de_champ_decorator.rb b/app/decorators/type_de_champ_decorator.rb index d54293195..32cbbbdd6 100644 --- a/app/decorators/type_de_champ_decorator.rb +++ b/app/decorators/type_de_champ_decorator.rb @@ -1,5 +1,4 @@ class TypeDeChampDecorator < Draper::Decorator - delegate_all def button_up params diff --git a/app/decorators/type_de_champ_private_decorator.rb b/app/decorators/type_de_champ_private_decorator.rb index 31d4d3299..ea9f715a2 100644 --- a/app/decorators/type_de_champ_private_decorator.rb +++ b/app/decorators/type_de_champ_private_decorator.rb @@ -1,3 +1,2 @@ class TypeDeChampPrivateDecorator < TypeDeChampDecorator - end diff --git a/app/facades/dossier_facades.rb b/app/facades/dossier_facades.rb index 6d707f7fc..dfb7f73ab 100644 --- a/app/facades/dossier_facades.rb +++ b/app/facades/dossier_facades.rb @@ -1,5 +1,4 @@ class DossierFacades - #TODO rechercher en fonction de la personne/email def initialize(dossier_id, email, champ_id = nil) @dossier = Dossier.find(dossier_id) diff --git a/app/facades/dossiers_list_facades.rb b/app/facades/dossiers_list_facades.rb index 0a05a4148..ac9cc952c 100644 --- a/app/facades/dossiers_list_facades.rb +++ b/app/facades/dossiers_list_facades.rb @@ -80,5 +80,4 @@ class DossiersListFacades def base_url liste @procedure.nil? ? backoffice_dossiers_path(liste: liste) : backoffice_dossiers_procedure_path(id: @procedure.id, liste: liste) end - end diff --git a/app/facades/invite_dossier_facades.rb b/app/facades/invite_dossier_facades.rb index ce52f0b3c..6aaad2450 100644 --- a/app/facades/invite_dossier_facades.rb +++ b/app/facades/invite_dossier_facades.rb @@ -1,5 +1,4 @@ class InviteDossierFacades < DossierFacades - #TODO rechercher en fonction de la personne/email def initialize id, email @dossier = Invite.where(email: email, id: id).first!.dossier diff --git a/app/lib/carto/geo_api/driver.rb b/app/lib/carto/geo_api/driver.rb index 01825be40..d37e17e87 100644 --- a/app/lib/carto/geo_api/driver.rb +++ b/app/lib/carto/geo_api/driver.rb @@ -1,7 +1,6 @@ module Carto module GeoAPI class Driver - def self.regions call regions_url end @@ -29,7 +28,6 @@ module Carto rescue RestClient::ServiceUnavailable nil end - end end end diff --git a/app/mailers/avis_mailer.rb b/app/mailers/avis_mailer.rb index cf435cefc..c84d6a74e 100644 --- a/app/mailers/avis_mailer.rb +++ b/app/mailers/avis_mailer.rb @@ -1,9 +1,7 @@ class AvisMailer < ApplicationMailer - def avis_invitation(avis) @avis = avis email = @avis.gestionnaire.try(:email) || @avis.email mail(to: email, subject: "Donnez votre avis sur le dossier nº #{@avis.dossier.id} (#{@avis.dossier.procedure.libelle})") end - end diff --git a/app/mailers/gestionnaire_mailer.rb b/app/mailers/gestionnaire_mailer.rb index 118537a8d..e5ea4ed91 100644 --- a/app/mailers/gestionnaire_mailer.rb +++ b/app/mailers/gestionnaire_mailer.rb @@ -1,5 +1,4 @@ class GestionnaireMailer < ApplicationMailer - def new_gestionnaire email, password send_mail email, password, "Vous avez été nommé accompagnateur sur la plateforme TPS" end diff --git a/app/mailers/invite_mailer.rb b/app/mailers/invite_mailer.rb index 09b58f229..a7d6e4d8d 100644 --- a/app/mailers/invite_mailer.rb +++ b/app/mailers/invite_mailer.rb @@ -1,5 +1,4 @@ class InviteMailer < ApplicationMailer - def invite_user invite vars_mailer invite diff --git a/app/models/champ_private.rb b/app/models/champ_private.rb index 6f7068148..118e58415 100644 --- a/app/models/champ_private.rb +++ b/app/models/champ_private.rb @@ -1,3 +1,2 @@ class ChampPrivate < Champ - end diff --git a/app/models/champ_public.rb b/app/models/champ_public.rb index 7b0efcb3d..c922614ae 100644 --- a/app/models/champ_public.rb +++ b/app/models/champ_public.rb @@ -1,3 +1,2 @@ class ChampPublic < Champ - end diff --git a/app/models/invite.rb b/app/models/invite.rb index de2e45fca..dde8b2c6f 100644 --- a/app/models/invite.rb +++ b/app/models/invite.rb @@ -1,5 +1,4 @@ class Invite < ActiveRecord::Base - belongs_to :dossier belongs_to :user diff --git a/app/models/invite_gestionnaire.rb b/app/models/invite_gestionnaire.rb index 6021286da..fabc49f36 100644 --- a/app/models/invite_gestionnaire.rb +++ b/app/models/invite_gestionnaire.rb @@ -1,3 +1,2 @@ class InviteGestionnaire < Invite - end diff --git a/app/models/invite_user.rb b/app/models/invite_user.rb index 05ec141e9..c599c2f23 100644 --- a/app/models/invite_user.rb +++ b/app/models/invite_user.rb @@ -1,3 +1,2 @@ class InviteUser < Invite - end diff --git a/app/models/type_de_champ_private.rb b/app/models/type_de_champ_private.rb index 1b4e7ed3f..ba6e89a82 100644 --- a/app/models/type_de_champ_private.rb +++ b/app/models/type_de_champ_private.rb @@ -1,3 +1,2 @@ class TypeDeChampPrivate < TypeDeChamp - end diff --git a/app/models/type_de_champ_public.rb b/app/models/type_de_champ_public.rb index 8e9fb760d..3be990e13 100644 --- a/app/models/type_de_champ_public.rb +++ b/app/models/type_de_champ_public.rb @@ -1,3 +1,2 @@ class TypeDeChampPublic < TypeDeChamp - end diff --git a/app/services/browser_service.rb b/app/services/browser_service.rb index a11c030c1..0163bbe6e 100644 --- a/app/services/browser_service.rb +++ b/app/services/browser_service.rb @@ -1,5 +1,4 @@ class BrowserService - def self.get_browser request BROWSER.value = Browser.new(request.user_agent) end @@ -15,5 +14,4 @@ class BrowserService true end - end diff --git a/app/services/dossier_service.rb b/app/services/dossier_service.rb index d6bdaddfc..bdd2e85ca 100644 --- a/app/services/dossier_service.rb +++ b/app/services/dossier_service.rb @@ -1,5 +1,4 @@ class DossierService - def initialize dossier, siret, france_connect_information @dossier = dossier @siret = siret diff --git a/app/services/dossiers_list_gestionnaire_service.rb b/app/services/dossiers_list_gestionnaire_service.rb index b110fde11..e705f5662 100644 --- a/app/services/dossiers_list_gestionnaire_service.rb +++ b/app/services/dossiers_list_gestionnaire_service.rb @@ -186,5 +186,4 @@ class DossiersListGestionnaireService def current_preference_smart_listing_page @current_devise_profil.preference_smart_listing_page end - end diff --git a/app/services/france_connect_salt_service.rb b/app/services/france_connect_salt_service.rb index f1d766e44..ac64ccbf1 100644 --- a/app/services/france_connect_salt_service.rb +++ b/app/services/france_connect_salt_service.rb @@ -1,5 +1,4 @@ class FranceConnectSaltService - attr_reader :model def initialize france_connect_information diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 27abe24cf..991120e95 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -1,5 +1,4 @@ class NotificationService - def initialize type_notif, dossier_id, attribut_change='' @type_notif = type_notif @dossier_id = dossier_id diff --git a/app/services/render_partial_service.rb b/app/services/render_partial_service.rb index efb5ed2ab..8e20fb8c6 100644 --- a/app/services/render_partial_service.rb +++ b/app/services/render_partial_service.rb @@ -1,5 +1,4 @@ class RenderPartialService - attr_accessor :controller, :method def initialize controller, method diff --git a/app/services/sync_credentials_service.rb b/app/services/sync_credentials_service.rb index 06d912b29..1319ae0d5 100644 --- a/app/services/sync_credentials_service.rb +++ b/app/services/sync_credentials_service.rb @@ -1,5 +1,4 @@ class SyncCredentialsService - def initialize klass, email_was, email, encrypted_password @klass = klass @email_was = email_was diff --git a/app/services/user_routes_authorization_service.rb b/app/services/user_routes_authorization_service.rb index 57b6f5af7..5507cdbbd 100644 --- a/app/services/user_routes_authorization_service.rb +++ b/app/services/user_routes_authorization_service.rb @@ -1,5 +1,4 @@ class UserRoutesAuthorizationService - def self.authorized_route? controller, dossier auth = controller.route_authorization diff --git a/app/uploaders/procedure_logo_uploader.rb b/app/uploaders/procedure_logo_uploader.rb index e5723f9a0..acce9da15 100644 --- a/app/uploaders/procedure_logo_uploader.rb +++ b/app/uploaders/procedure_logo_uploader.rb @@ -1,7 +1,6 @@ # encoding: utf-8 class ProcedureLogoUploader < BaseUploader - def root File.join(Rails.root, "public") end @@ -47,5 +46,4 @@ class ProcedureLogoUploader < BaseUploader def generate_secure_token SecureRandom.uuid end - end diff --git a/app/validators/email_format_validator.rb b/app/validators/email_format_validator.rb index 33f6b6065..bc9f01cb5 100644 --- a/app/validators/email_format_validator.rb +++ b/app/validators/email_format_validator.rb @@ -1,5 +1,4 @@ class EmailFormatValidator < ActiveModel::Validator - def email_regex /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i end diff --git a/app/validators/procedure_path_format_validator.rb b/app/validators/procedure_path_format_validator.rb index 2d677c37e..974f4c404 100644 --- a/app/validators/procedure_path_format_validator.rb +++ b/app/validators/procedure_path_format_validator.rb @@ -1,5 +1,4 @@ class ProcedurePathFormatValidator < ActiveModel::Validator - def path_regex /^[a-z0-9_]{3,30}$/ end @@ -8,5 +7,4 @@ class ProcedurePathFormatValidator < ActiveModel::Validator return false if record.path.blank? record.errors[:path] << "Path invalide" unless path_regex.match(record.path) end - end diff --git a/config/initializers/stringupcasepatch.rb b/config/initializers/stringupcasepatch.rb index 0c888e580..39d2e107a 100644 --- a/config/initializers/stringupcasepatch.rb +++ b/config/initializers/stringupcasepatch.rb @@ -1,9 +1,7 @@ require "unicode_utils/upcase" class String - def upcase UnicodeUtils.upcase(self) end - end diff --git a/db/migrate/20151207140202_update_module_api_carto.rb b/db/migrate/20151207140202_update_module_api_carto.rb index 0bb9b77ab..25549224d 100644 --- a/db/migrate/20151207140202_update_module_api_carto.rb +++ b/db/migrate/20151207140202_update_module_api_carto.rb @@ -1,11 +1,8 @@ class UpdateModuleAPICarto < ActiveRecord::Migration - class Procedure < ActiveRecord::Base - end class ModuleAPICarto < ActiveRecord::Base - end def up diff --git a/db/migrate/20160120094750_create_france_connect_information.rb b/db/migrate/20160120094750_create_france_connect_information.rb index 91b47ec10..1cea47245 100644 --- a/db/migrate/20160120094750_create_france_connect_information.rb +++ b/db/migrate/20160120094750_create_france_connect_information.rb @@ -1,11 +1,8 @@ class CreateFranceConnectInformation < ActiveRecord::Migration - class User < ActiveRecord::Base - end class FranceConnectInformation < ActiveRecord::Base - end def up diff --git a/db/migrate/20160317144949_piece_justificative_have_user.rb b/db/migrate/20160317144949_piece_justificative_have_user.rb index 10ee2b45b..23ddd81a3 100644 --- a/db/migrate/20160317144949_piece_justificative_have_user.rb +++ b/db/migrate/20160317144949_piece_justificative_have_user.rb @@ -8,7 +8,6 @@ class PieceJustificativeHaveUser < ActiveRecord::Migration end class User < ActiveRecord::Base - end def change diff --git a/db/migrate/20160317153115_cerfa_have_user.rb b/db/migrate/20160317153115_cerfa_have_user.rb index 7a0710901..a50cbf4c4 100644 --- a/db/migrate/20160317153115_cerfa_have_user.rb +++ b/db/migrate/20160317153115_cerfa_have_user.rb @@ -8,7 +8,6 @@ class CerfaHaveUser < ActiveRecord::Migration end class User < ActiveRecord::Base - end def change diff --git a/db/migrate/20160524093540_set_original_filename_for_uploaders.rb b/db/migrate/20160524093540_set_original_filename_for_uploaders.rb index d9d6306c6..bf8e700f2 100644 --- a/db/migrate/20160524093540_set_original_filename_for_uploaders.rb +++ b/db/migrate/20160524093540_set_original_filename_for_uploaders.rb @@ -1,10 +1,8 @@ class SetOriginalFilenameForUploaders < ActiveRecord::Migration class PieceJustificative < ActiveRecord::Base - end class Cerfa < ActiveRecord::Base - end def change diff --git a/db/migrate/20160609145737_delete_default_description_to_dossier.rb b/db/migrate/20160609145737_delete_default_description_to_dossier.rb index ded5122e7..8ba2fc7d9 100644 --- a/db/migrate/20160609145737_delete_default_description_to_dossier.rb +++ b/db/migrate/20160609145737_delete_default_description_to_dossier.rb @@ -1,18 +1,14 @@ class DeleteDefaultDescriptionToDossier < ActiveRecord::Migration class Dossier < ActiveRecord::Base - end class Champ < ActiveRecord::Base - end class Procedure < ActiveRecord::Base - end class TypeDeChamp < ActiveRecord::Base - end def up diff --git a/db/migrate/20160622081321_convert_all_datetime_to_date_on_database.rb b/db/migrate/20160622081321_convert_all_datetime_to_date_on_database.rb index 7d6d09bd0..2c61b98c2 100644 --- a/db/migrate/20160622081321_convert_all_datetime_to_date_on_database.rb +++ b/db/migrate/20160622081321_convert_all_datetime_to_date_on_database.rb @@ -1,7 +1,5 @@ class ConvertAllDatetimeToDateOnDatabase < ActiveRecord::Migration - class TypeDeChamp < ActiveRecord::Base - end def change diff --git a/db/migrate/20160803081304_fix_default_type_on_type_de_champ_table.rb b/db/migrate/20160803081304_fix_default_type_on_type_de_champ_table.rb index 01636f7c6..56120ef60 100644 --- a/db/migrate/20160803081304_fix_default_type_on_type_de_champ_table.rb +++ b/db/migrate/20160803081304_fix_default_type_on_type_de_champ_table.rb @@ -1,6 +1,5 @@ class FixDefaultTypeOnTypeDeChampTable < ActiveRecord::Migration class TypeDeChamp < ActiveRecord::Base - end def up diff --git a/db/migrate/20160804130638_add_type_attr_in_champ_table.rb b/db/migrate/20160804130638_add_type_attr_in_champ_table.rb index 465d9ce63..df21b33ea 100644 --- a/db/migrate/20160804130638_add_type_attr_in_champ_table.rb +++ b/db/migrate/20160804130638_add_type_attr_in_champ_table.rb @@ -1,5 +1,4 @@ class AddTypeAttrInChampTable < ActiveRecord::Migration - class TypeDeChamp < ActiveRecord::Base has_many :champs end diff --git a/db/migrate/20160822142045_delete_value_of_filter_procedure.rb b/db/migrate/20160822142045_delete_value_of_filter_procedure.rb index f541aefc9..9a369b8ed 100644 --- a/db/migrate/20160822142045_delete_value_of_filter_procedure.rb +++ b/db/migrate/20160822142045_delete_value_of_filter_procedure.rb @@ -1,6 +1,5 @@ class DeleteValueOfFilterProcedure < ActiveRecord::Migration class Gestionnaire < ActiveRecord::Base - end def change diff --git a/db/migrate/20160901082824_initiated_all_received_mail_for_procedure.rb b/db/migrate/20160901082824_initiated_all_received_mail_for_procedure.rb index 89e5f03cd..a2ccc89d9 100644 --- a/db/migrate/20160901082824_initiated_all_received_mail_for_procedure.rb +++ b/db/migrate/20160901082824_initiated_all_received_mail_for_procedure.rb @@ -4,7 +4,6 @@ class InitiatedAllReceivedMailForProcedure < ActiveRecord::Migration end class MailTemplate < ActiveRecord::Base - end class ::MailReceived < MailTemplate diff --git a/db/migrate/20160927154248_change_emptyto_nil_in_preference_list_dossier_table.rb b/db/migrate/20160927154248_change_emptyto_nil_in_preference_list_dossier_table.rb index a5905c8e2..e4203c62c 100644 --- a/db/migrate/20160927154248_change_emptyto_nil_in_preference_list_dossier_table.rb +++ b/db/migrate/20160927154248_change_emptyto_nil_in_preference_list_dossier_table.rb @@ -1,6 +1,5 @@ class ChangeEmptytoNilInPreferenceListDossierTable < ActiveRecord::Migration class PreferenceListDossier < ActiveRecord::Base - end def change diff --git a/db/migrate/20161004175442_fix_type_de_champ_header_section_with_mandatory_true.rb b/db/migrate/20161004175442_fix_type_de_champ_header_section_with_mandatory_true.rb index 945d4a910..23aec6f90 100644 --- a/db/migrate/20161004175442_fix_type_de_champ_header_section_with_mandatory_true.rb +++ b/db/migrate/20161004175442_fix_type_de_champ_header_section_with_mandatory_true.rb @@ -1,6 +1,5 @@ class FixTypeDeChampHeaderSectionWithMandatoryTrue < ActiveRecord::Migration class TypeDeChamp < ActiveRecord::Base - end def change diff --git a/db/migrate/20161007095443_reset_all_preference_list_dossier.rb b/db/migrate/20161007095443_reset_all_preference_list_dossier.rb index f4594bd5d..677ac4be6 100644 --- a/db/migrate/20161007095443_reset_all_preference_list_dossier.rb +++ b/db/migrate/20161007095443_reset_all_preference_list_dossier.rb @@ -143,7 +143,6 @@ class ResetAllPreferenceListDossier < ActiveRecord::Migration class Procedure < ActiveRecord::Base has_many :assign_to, dependent: :destroy has_many :gestionnaires, through: :assign_to - end def change diff --git a/spec/mailers/previews/avis_mailer_preview.rb b/spec/mailers/previews/avis_mailer_preview.rb index 55d153cd1..be78eae74 100644 --- a/spec/mailers/previews/avis_mailer_preview.rb +++ b/spec/mailers/previews/avis_mailer_preview.rb @@ -1,8 +1,6 @@ # Preview all emails at http://localhost:3000/rails/mailers/avis_mailer class AvisMailerPreview < ActionMailer::Preview - def avis_invitation AvisMailer.avis_invitation(Avis.last) end - end diff --git a/spec/mailers/previews/notification_mailer_preview.rb b/spec/mailers/previews/notification_mailer_preview.rb index 3cb6079a2..63691a2cd 100644 --- a/spec/mailers/previews/notification_mailer_preview.rb +++ b/spec/mailers/previews/notification_mailer_preview.rb @@ -1,7 +1,5 @@ class NotificationMailerPreview < ActionMailer::Preview - def send_notification NotificationMailer.send_notification(Dossier.last, Dossier.last.procedure.initiated_mail_template) end - end From 82eb66cfe8738c79f3f0497e48cc7d5038c56131 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 16:06:07 +0200 Subject: [PATCH 018/136] Enable the Layout/EmptyLinesAroundMethodBody cop --- .rubocop.yml | 2 +- app/controllers/admin/procedures_controller.rb | 1 - app/controllers/cgu_controller.rb | 1 - app/mailers/new_admin_mailer.rb | 1 - app/mailers/welcome_mailer.rb | 1 - app/models/gestionnaire.rb | 1 - app/services/clamav_service.rb | 1 - app/services/dossiers_list_gestionnaire_service.rb | 1 - app/services/geojson_service.rb | 1 - .../20151102104309_change_state_confirmed_to_validated.rb | 1 - db/migrate/20151102163051_delete_attributs_to_dossier.rb | 1 - .../20160802113112_build_default_preference_list_dossier.rb | 1 - db/migrate/20161007095443_reset_all_preference_list_dossier.rb | 1 - 13 files changed, 1 insertion(+), 13 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 3a537645c..b3ce83106 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -61,7 +61,7 @@ Layout/EmptyLinesAroundExceptionHandlingKeywords: Enabled: false Layout/EmptyLinesAroundMethodBody: - Enabled: false + Enabled: true Layout/EmptyLinesAroundModuleBody: Enabled: false diff --git a/app/controllers/admin/procedures_controller.rb b/app/controllers/admin/procedures_controller.rb index 3ca4c0fb9..2b7cb2adf 100644 --- a/app/controllers/admin/procedures_controller.rb +++ b/app/controllers/admin/procedures_controller.rb @@ -40,7 +40,6 @@ class Admin::ProceduresController < AdminController end def edit - end def destroy diff --git a/app/controllers/cgu_controller.rb b/app/controllers/cgu_controller.rb index 2c7b968d8..4d3f2e1ed 100644 --- a/app/controllers/cgu_controller.rb +++ b/app/controllers/cgu_controller.rb @@ -1,5 +1,4 @@ class CguController < ApplicationController def index - end end diff --git a/app/mailers/new_admin_mailer.rb b/app/mailers/new_admin_mailer.rb index 4527451a0..673b2a53a 100644 --- a/app/mailers/new_admin_mailer.rb +++ b/app/mailers/new_admin_mailer.rb @@ -1,6 +1,5 @@ class NewAdminMailer < ApplicationMailer def new_admin_email admin, password - @admin = admin @password = password diff --git a/app/mailers/welcome_mailer.rb b/app/mailers/welcome_mailer.rb index 4306ee974..789b494b6 100644 --- a/app/mailers/welcome_mailer.rb +++ b/app/mailers/welcome_mailer.rb @@ -1,6 +1,5 @@ class WelcomeMailer < ApplicationMailer def welcome_email user - @user = user mail(to: user.email, diff --git a/app/models/gestionnaire.rb b/app/models/gestionnaire.rb index d010bdcf5..2b971e7d1 100644 --- a/app/models/gestionnaire.rb +++ b/app/models/gestionnaire.rb @@ -52,7 +52,6 @@ class Gestionnaire < ActiveRecord::Base end def build_default_preferences_list_dossier procedure_id=nil - PreferenceListDossier.available_columns_for(procedure_id).each do |table| table.second.each do |column| if valid_couple_table_attr? table.first, column.first diff --git a/app/services/clamav_service.rb b/app/services/clamav_service.rb index 5d1142c96..022f02de1 100644 --- a/app/services/clamav_service.rb +++ b/app/services/clamav_service.rb @@ -1,6 +1,5 @@ class ClamavService def self.safe_file? path_file - if Rails.env == 'development' return CLAMAV[:response] if CLAMAV[:mock?] end diff --git a/app/services/dossiers_list_gestionnaire_service.rb b/app/services/dossiers_list_gestionnaire_service.rb index e705f5662..1cd0bd593 100644 --- a/app/services/dossiers_list_gestionnaire_service.rb +++ b/app/services/dossiers_list_gestionnaire_service.rb @@ -13,7 +13,6 @@ class DossiersListGestionnaireService 'termine' => termine, 'archive' => archive, 'all_state' => all_state}[@liste] - end def self.dossiers_liste_libelle diff --git a/app/services/geojson_service.rb b/app/services/geojson_service.rb index 7bc175fbe..1f6562089 100644 --- a/app/services/geojson_service.rb +++ b/app/services/geojson_service.rb @@ -11,7 +11,6 @@ class GeojsonService end def self.to_json_polygon_for_cadastre coordinates - polygon = { geom: { type: "Feature", diff --git a/db/migrate/20151102104309_change_state_confirmed_to_validated.rb b/db/migrate/20151102104309_change_state_confirmed_to_validated.rb index 4fbeff150..e19e39ed8 100644 --- a/db/migrate/20151102104309_change_state_confirmed_to_validated.rb +++ b/db/migrate/20151102104309_change_state_confirmed_to_validated.rb @@ -1,6 +1,5 @@ class ChangeStateConfirmedToValidated < ActiveRecord::Migration def change Dossier.where(state: 'confirmed').update_all(state: 'validated') - end end diff --git a/db/migrate/20151102163051_delete_attributs_to_dossier.rb b/db/migrate/20151102163051_delete_attributs_to_dossier.rb index c7752598b..8928f910b 100644 --- a/db/migrate/20151102163051_delete_attributs_to_dossier.rb +++ b/db/migrate/20151102163051_delete_attributs_to_dossier.rb @@ -7,6 +7,5 @@ class DeleteAttributsToDossier < ActiveRecord::Migration remove_column :dossiers, :position_lon remove_column :dossiers, :ref_dossier_carto - end end diff --git a/db/migrate/20160802113112_build_default_preference_list_dossier.rb b/db/migrate/20160802113112_build_default_preference_list_dossier.rb index a57a5cbfe..2a11ea8f0 100644 --- a/db/migrate/20160802113112_build_default_preference_list_dossier.rb +++ b/db/migrate/20160802113112_build_default_preference_list_dossier.rb @@ -1,7 +1,6 @@ class BuildDefaultPreferenceListDossier < ActiveRecord::Migration class Gestionnaire < ActiveRecord::Base def build_default_preferences_list_dossier - PreferenceListDossier.available_columns.each do |table| table.second.each do |column| if valid_couple_table_attr? table.first, column.first diff --git a/db/migrate/20161007095443_reset_all_preference_list_dossier.rb b/db/migrate/20161007095443_reset_all_preference_list_dossier.rb index 677ac4be6..137cf286b 100644 --- a/db/migrate/20161007095443_reset_all_preference_list_dossier.rb +++ b/db/migrate/20161007095443_reset_all_preference_list_dossier.rb @@ -96,7 +96,6 @@ class ResetAllPreferenceListDossier < ActiveRecord::Migration has_many :procedures, through: :assign_to def build_default_preferences_list_dossier procedure_id=nil - PreferenceListDossier.available_columns_for(procedure_id).each do |table| table.second.each do |column| if valid_couple_table_attr? table.first, column.first From f1d66e40c3c2f85cb82ee14699aa7a3f3f310038 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 16:07:05 +0200 Subject: [PATCH 019/136] Enable the Layout/EmptyLinesAroundModuleBody cop --- .rubocop.yml | 2 +- app/models/concerns/credentials_syncable_concern.rb | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index b3ce83106..cdbbfd8e0 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -64,7 +64,7 @@ Layout/EmptyLinesAroundMethodBody: Enabled: true Layout/EmptyLinesAroundModuleBody: - Enabled: false + Enabled: true Layout/EndOfLine: Enabled: true diff --git a/app/models/concerns/credentials_syncable_concern.rb b/app/models/concerns/credentials_syncable_concern.rb index f1ee62df8..f5bf6ae92 100644 --- a/app/models/concerns/credentials_syncable_concern.rb +++ b/app/models/concerns/credentials_syncable_concern.rb @@ -15,5 +15,4 @@ module CredentialsSyncableConcern def force_sync_credentials SyncCredentialsService.new(self.class, email_was, email, encrypted_password).change_credentials! end - end From d248afc3760bc3d86216239d76353b1c97de5df2 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 12 Jun 2017 16:12:03 +0200 Subject: [PATCH 020/136] Enable the Layout/ExtraSpacing cop --- .rubocop.yml | 5 ++++- app/controllers/stats_controller.rb | 2 +- .../users/dossiers/add_siret_controller.rb | 2 +- config/initializers/smart_listing.rb | 2 +- ...0160822142045_delete_value_of_filter_procedure.rb | 2 +- lib/tasks/dev.rake | 12 ++++++------ spec/controllers/users/dossiers_controller_spec.rb | 8 ++++---- spec/decorators/procedure_decorator_spec.rb | 2 +- spec/facades/admin_procedures_show_facades_spec.rb | 4 ++-- spec/features/backoffice/connection_spec.rb | 6 +++--- spec/features/backoffice/search_file_spec.rb | 4 ++-- spec/models/administrateur_spec.rb | 2 +- spec/services/accompagnateur_service_spec.rb | 2 +- .../admin/types_de_champ/show.html.haml_spec.rb | 2 +- .../types_de_champ_private/show.html.haml_spec.rb | 2 +- .../show.html.haml_spec.rb | 2 +- .../views/backoffice/dossiers/show.html.html_spec.rb | 2 +- spec/views/dossiers/_infos_dossier_spec.rb | 2 +- spec/views/users/carte/show.html.haml_spec.rb | 2 +- 19 files changed, 34 insertions(+), 31 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index cdbbfd8e0..1092ac6c6 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -71,7 +71,10 @@ Layout/EndOfLine: EnforcedStyle: lf Layout/ExtraSpacing: - Enabled: false + Enabled: true + Exclude: + - "Guardfile" + - "db/schema.rb" Layout/FirstArrayElementLineBreak: Enabled: false diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb index eb96939c1..5cacd22d1 100644 --- a/app/controllers/stats_controller.rb +++ b/app/controllers/stats_controller.rb @@ -61,7 +61,7 @@ class StatsController < ApplicationController count_per_administrateur = procedures.group(:administrateur_id).count.values { 'Une procédure' => count_per_administrateur.select { |count| count == 1 }.count, - 'Entre deux et cinq procédures' => count_per_administrateur.select { |count| 2 <= count && count <= 5 }.count, + 'Entre deux et cinq procédures' => count_per_administrateur.select { |count| 2 <= count && count <= 5 }.count, 'Plus de cinq procédures' => count_per_administrateur.select { |count| 5 < count }.count } end diff --git a/app/controllers/users/dossiers/add_siret_controller.rb b/app/controllers/users/dossiers/add_siret_controller.rb index 4190e304f..92ab6ee45 100644 --- a/app/controllers/users/dossiers/add_siret_controller.rb +++ b/app/controllers/users/dossiers/add_siret_controller.rb @@ -1,6 +1,6 @@ class Users::Dossiers::AddSiretController < ApplicationController def show - @facade = DossierFacades.new params[:dossier_id], current_user.email + @facade = DossierFacades.new params[:dossier_id], current_user.email raise ActiveRecord::RecordNotFound unless @facade.procedure.individual_with_siret? diff --git a/config/initializers/smart_listing.rb b/config/initializers/smart_listing.rb index 91d459d6d..09a097d25 100644 --- a/config/initializers/smart_listing.rb +++ b/config/initializers/smart_listing.rb @@ -10,7 +10,7 @@ SmartListing.configure do |config| # :unlimited_per_page => false, # allow infinite page size # :paginate => true, # allow pagination # :memorize_per_page => false, # save per page settings in the cookie - :page_sizes => [10, 20, 50, 100], # set available page sizes array + :page_sizes => [10, 20, 50, 100], # set available page sizes array # :kaminari_options => {:theme => "smart_listing"}, # Kaminari's paginate helper options }) diff --git a/db/migrate/20160822142045_delete_value_of_filter_procedure.rb b/db/migrate/20160822142045_delete_value_of_filter_procedure.rb index 9a369b8ed..30a12da5f 100644 --- a/db/migrate/20160822142045_delete_value_of_filter_procedure.rb +++ b/db/migrate/20160822142045_delete_value_of_filter_procedure.rb @@ -1,5 +1,5 @@ class DeleteValueOfFilterProcedure < ActiveRecord::Migration - class Gestionnaire < ActiveRecord::Base + class Gestionnaire < ActiveRecord::Base end def change diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake index 3373efb79..cd9f5eb23 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -1,6 +1,6 @@ namespace :dev do desc 'Initialise dev environment' - task :init do + task :init do puts 'start initialisation' Rake::Task['dev:generate_token_file'].invoke Rake::Task['dev:generate_franceconnect_file'].invoke @@ -10,10 +10,10 @@ namespace :dev do puts 'end initialisation' end - task :generate_token_file do + task :generate_token_file do puts 'creating token.rb file' res = `rake secret`.gsub("\n", '') - file = File.new('config/initializers/token.rb', 'w+') + file = File.new('config/initializers/token.rb', 'w+') comment = < Date: Tue, 13 Jun 2017 11:27:41 +0200 Subject: [PATCH 021/136] =?UTF-8?q?Don=E2=80=99t=20run=20rubocop=20on=20db?= =?UTF-8?q?/schema.rb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .rubocop.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index 1092ac6c6..ede1689a7 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,3 +1,7 @@ +AllCops: + Exclude: + - "db/schema.rb" + Bundler/DuplicatedGem: Enabled: true @@ -74,7 +78,6 @@ Layout/ExtraSpacing: Enabled: true Exclude: - "Guardfile" - - "db/schema.rb" Layout/FirstArrayElementLineBreak: Enabled: false From 6f884e9740b53fb6876ab12ed1e1dff0b1cc58e8 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 13 Jun 2017 11:04:22 +0200 Subject: [PATCH 022/136] Add the rubocop gem --- Gemfile | 1 + Gemfile.lock | 1 + 2 files changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index 246e589dd..ea875153b 100644 --- a/Gemfile +++ b/Gemfile @@ -122,6 +122,7 @@ group :development do gem 'web-console' gem 'rack-handlers' gem 'xray-rails' + gem 'rubocop', require: false gem 'haml-lint' gem 'scss_lint', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index 50ff1bd59..5981b90c6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -714,6 +714,7 @@ DEPENDENCIES rest-client rgeo-geojson rspec-rails (~> 3.0) + rubocop sass-rails (~> 5.0) scenic scss_lint From f5d4686b809b172cda8a631d4d733e94b7766712 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 13 Jun 2017 10:58:24 +0200 Subject: [PATCH 023/136] Document the rubocop usage in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 61d22b1c9..02e3a3db2 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ Pour exécuter les tests de l'application, plusieurs possibilités : ## Linting +- Faire tourner RuboCop : `bundle exec rubocop` - Linter les fichiers HAML : `bundle exec haml-lint app/views/` - Linter les fichiers SCSS : `bundle exec scss-lint app/assets/stylesheets/` From e635ebbce22a18d0a809d5ed3e632f9e4d471ba6 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 13 Jun 2017 10:58:46 +0200 Subject: [PATCH 024/136] Make CircleCI run RuboCop --- .circleci/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 45417e72c..c44ceede5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -53,6 +53,9 @@ jobs: command: | TESTFILES=$(circleci tests glob "spec/**/*.rb"| xargs -n 1 echo | grep -v "spec/factories/" | tr " " "\n" | circleci tests split --split-by=timings) bundle exec rspec --color --require spec_helper -- ${TESTFILES} + - run: + name: Run rubocop + command: bundle exec rubocop - run: name: Run haml-lint command: bundle exec haml-lint app/views/ From 1bff7d19147af80455d980f8c18ccb010bf1bec6 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Fri, 2 Jun 2017 10:46:18 +0200 Subject: [PATCH 025/136] Fix incorrect Backoffice::DossiersController tests --- spec/controllers/backoffice/dossiers_controller_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/controllers/backoffice/dossiers_controller_spec.rb b/spec/controllers/backoffice/dossiers_controller_spec.rb index b21bd6e32..a8599bac5 100644 --- a/spec/controllers/backoffice/dossiers_controller_spec.rb +++ b/spec/controllers/backoffice/dossiers_controller_spec.rb @@ -252,7 +252,7 @@ describe Backoffice::DossiersController, type: :controller do describe 'POST #refuse' do before do - dossier.refused! + dossier.received! sign_in gestionnaire end @@ -278,7 +278,7 @@ describe Backoffice::DossiersController, type: :controller do describe 'POST #without_continuation' do before do - dossier.without_continuation! + dossier.received! sign_in gestionnaire end subject { post :without_continuation, params: {dossier_id: dossier_id} } From 33944e343ac8ff8badb095ab3f7452f48994b91a Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Thu, 1 Jun 2017 21:23:47 +0200 Subject: [PATCH 026/136] Add Backoffice::DossiersController#process_dossier and use it --- .../backoffice/dossiers_controller.rb | 11 +++++++ ...ckoffice_dossierscontroller_show.html.haml | 23 ++++++++------ config/routes.rb | 1 + .../backoffice/dossiers_controller_spec.rb | 31 +++++++++++++++++++ ...backoffice_dossierscontroller_show_spec.rb | 6 ++-- 5 files changed, 59 insertions(+), 13 deletions(-) diff --git a/app/controllers/backoffice/dossiers_controller.rb b/app/controllers/backoffice/dossiers_controller.rb index ec3524100..d0409acad 100644 --- a/app/controllers/backoffice/dossiers_controller.rb +++ b/app/controllers/backoffice/dossiers_controller.rb @@ -103,6 +103,17 @@ class Backoffice::DossiersController < Backoffice::DossiersListController redirect_to backoffice_dossier_path(id: dossier.id) end + def process_dossier + case params[:process_action] + when "refuse" + refuse + when "without_continuation" + without_continuation + when "close" + close + end + end + def refuse create_dossier_facade params[:dossier_id] diff --git a/app/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.html.haml b/app/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.html.haml index 2560a0bb8..24208562b 100644 --- a/app/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.html.haml +++ b/app/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.html.haml @@ -8,16 +8,19 @@ = link_to 'Passer en instruction', backoffice_dossier_receive_path(@facade.dossier), method: :post, class: 'btn btn-danger btn-block', data: { confirm: "Confirmer vous le passage en instruction de ce dossier ?" } - elsif @facade.dossier.received? - %ul.list-inline - %li - = link_to url_for({ controller: 'backoffice/dossiers', action: :close, dossier_id: @facade.dossier.id }), class: 'btn btn-success', method: :post, title: 'Accepter', data: { toggle: :tooltip, confirm: "Accepter ce dossier ?" } do - %i.fa.fa-check - %li - = link_to url_for({ controller: 'backoffice/dossiers', action: :without_continuation, dossier_id: @facade.dossier.id }), class: 'btn btn-warning', method: :post, title: 'Classer sans suite', data: { toggle: :tooltip, confirm: "Classer sans suite ce dossier ?" } do - %i.fa.fa-circle-o - %li - = link_to url_for({ controller: 'backoffice/dossiers', action: :refuse, dossier_id: @facade.dossier.id }), class: 'btn btn-danger', method: :post, title: 'Refuser', data: { toggle: :tooltip, confirm: "Refuser ce dossier ?" } do - %i.fa.fa-times + = form_tag(backoffice_dossier_process_dossier_url(@facade.dossier.id), method: :post) do + %ul.list-inline + %li + = button_tag name: :process_action, value: "close", class: 'btn btn-success', title: 'Accepter', data: { toggle: :tooltip, confirm: "Accepter ce dossier ?" } do + %i.fa.fa-check + + %li + = button_tag name: :process_action, value: "without_continuation", class: 'btn btn-warning', title: 'Classer sans suite', data: { toggle: :tooltip, confirm: "Classer sans suite ce dossier ?" } do + %i.fa.fa-circle-o + + %li + = button_tag name: :process_action, value: "refuse", class: 'btn btn-danger', title: 'Refuser', data: { toggle: :tooltip, confirm: "Refuser ce dossier ?" } do + %i.fa.fa-times = link_to 'Reouvrir', backoffice_dossier_reopen_path(@facade.dossier), method: :post, class: 'btn btn-default btn-block', data: { confirm: "Confirmer vous la réouverture de ce dossier ?" } diff --git a/config/routes.rb b/config/routes.rb index 7aff0071f..00cd47949 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -172,6 +172,7 @@ Rails.application.routes.draw do resources :dossiers do post 'receive' => 'dossiers#receive' + post 'process_dossier' => 'dossiers#process_dossier' post 'refuse' => 'dossiers#refuse' post 'without_continuation' => 'dossiers#without_continuation' post 'close' => 'dossiers#close' diff --git a/spec/controllers/backoffice/dossiers_controller_spec.rb b/spec/controllers/backoffice/dossiers_controller_spec.rb index a8599bac5..2edff6017 100644 --- a/spec/controllers/backoffice/dossiers_controller_spec.rb +++ b/spec/controllers/backoffice/dossiers_controller_spec.rb @@ -250,6 +250,37 @@ describe Backoffice::DossiersController, type: :controller do it { is_expected.to redirect_to backoffice_dossier_path(id: dossier.id) } end + describe 'POST #process_dossier' do + before do + dossier.received! + sign_in gestionnaire + end + + context "with refuse" do + it "calls the refuse method" do + expect(controller).to receive(:refuse) + + post :process_dossier, params: { dossier_id: dossier_id, process_action: "refuse" } + end + end + + context "with without_continuation" do + it "calls the without_continuation method" do + expect(controller).to receive(:without_continuation) + + post :process_dossier, params: { dossier_id: dossier_id, process_action: "without_continuation" } + end + end + + context "with close" do + it "calls the close method" do + expect(controller).to receive(:close) + + post :process_dossier, params: { dossier_id: dossier_id, process_action: "close" } + end + end + end + describe 'POST #refuse' do before do dossier.received! diff --git a/spec/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show_spec.rb b/spec/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show_spec.rb index cd91a032c..4682bb331 100644 --- a/spec/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show_spec.rb +++ b/spec/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show_spec.rb @@ -74,9 +74,9 @@ describe 'layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.htm it { expect(rendered).to have_content('En instruction') } it 'button accepter / refuser / classer sans suite are present' do - expect(rendered).to have_css('a[title="Accepter"]') - expect(rendered).to have_css('a[title="Classer sans suite"]') - expect(rendered).to have_css('a[title="Refuser"]') + expect(rendered).to have_css('button[title="Accepter"]') + expect(rendered).to have_css('button[title="Classer sans suite"]') + expect(rendered).to have_css('button[title="Refuser"]') end end From 12740b4a3efe0a1f91bc43d7b22845578a5081ab Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 30 May 2017 16:37:12 +0200 Subject: [PATCH 027/136] [Fix #197] Add the motivation column to Dossiers --- db/migrate/20170530141608_add_motivation_to_dossier.rb | 5 +++++ db/schema.rb | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20170530141608_add_motivation_to_dossier.rb diff --git a/db/migrate/20170530141608_add_motivation_to_dossier.rb b/db/migrate/20170530141608_add_motivation_to_dossier.rb new file mode 100644 index 000000000..6ee802630 --- /dev/null +++ b/db/migrate/20170530141608_add_motivation_to_dossier.rb @@ -0,0 +1,5 @@ +class AddMotivationToDossier < ActiveRecord::Migration[5.0] + def change + add_column :dossiers, :motivation, :text + end +end diff --git a/db/schema.rb b/db/schema.rb index 0fd7a56a4..b24616cc2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170523092900) do +ActiveRecord::Schema.define(version: 20170530141608) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -151,6 +151,7 @@ ActiveRecord::Schema.define(version: 20170523092900) do t.datetime "initiated_at" t.datetime "received_at" t.datetime "processed_at" + t.text "motivation" t.index ["procedure_id"], name: "index_dossiers_on_procedure_id", using: :btree t.index ["user_id"], name: "index_dossiers_on_user_id", using: :btree end From 88267a990d82b59953f7ea0596776987f20f7c8f Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Thu, 1 Jun 2017 11:26:21 +0200 Subject: [PATCH 028/136] =?UTF-8?q?[Fix=20#197]=20Show=20the=20motivation?= =?UTF-8?q?=20on=20a=20dossier=E2=80=99s=20details?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/views/dossiers/_dossier_show.html.haml | 2 ++ app/views/dossiers/_motivation.html.haml | 11 +++++++++++ spec/views/backoffice/dossiers/show.html.html_spec.rb | 8 ++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 app/views/dossiers/_motivation.html.haml diff --git a/app/views/dossiers/_dossier_show.html.haml b/app/views/dossiers/_dossier_show.html.haml index cfbe4ee02..60d426479 100644 --- a/app/views/dossiers/_dossier_show.html.haml +++ b/app/views/dossiers/_dossier_show.html.haml @@ -2,6 +2,8 @@ = render partial: 'dossiers/messagerie', locals: { dossier_facade: @facade } += render partial: 'dossiers/motivation', locals: { dossier_facade: @facade } + - if @facade.procedure.individual_with_siret .default-data-block .row.show-block.infos diff --git a/app/views/dossiers/_motivation.html.haml b/app/views/dossiers/_motivation.html.haml new file mode 100644 index 000000000..dad4f1b5c --- /dev/null +++ b/app/views/dossiers/_motivation.html.haml @@ -0,0 +1,11 @@ +- if Dossier::TERMINE.include?(@facade.dossier.state) && @facade.dossier.motivation.present? + .default-data-block.default_visible + .row.show-block.infos + .header + .col-xs-12.title + .carret-right + .carret-down + MOTIVATION + .body + .display-block-on-print + = @facade.dossier.motivation diff --git a/spec/views/backoffice/dossiers/show.html.html_spec.rb b/spec/views/backoffice/dossiers/show.html.html_spec.rb index 4060d600f..0be7ed6f9 100644 --- a/spec/views/backoffice/dossiers/show.html.html_spec.rb +++ b/spec/views/backoffice/dossiers/show.html.html_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' describe 'backoffice/dossiers/show.html.haml', type: :view do - let!(:dossier) { create(:dossier, :with_entreprise, state: state) } - let(:state) { 'draft' } + let!(:dossier) { create(:dossier, :with_entreprise, state: state, motivation: "Motivation de décision") } + let(:state) { 'closed' } let(:dossier_id) { dossier.id } let(:gestionnaire) { create(:gestionnaire) } @@ -44,5 +44,9 @@ describe 'backoffice/dossiers/show.html.haml', type: :view do expect(rendered).to_not have_css('#maj_infos') end end + + it "shows the motivation" do + expect(rendered).to have_content("Motivation de décision") + end end end From b58c02180c7e0c0ef052e88f3c5155d826c06c01 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 13 Jun 2017 11:50:06 +0200 Subject: [PATCH 029/136] [Fix #197] Allow a gestionnaire to add a motivation --- app/assets/stylesheets/left_panel.scss | 5 +++++ .../backoffice/dossiers_controller.rb | 18 +++++++++++++++--- app/models/dossier.rb | 17 ++++++++++++++++- ...ackoffice_dossierscontroller_show.html.haml | 2 ++ 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/left_panel.scss b/app/assets/stylesheets/left_panel.scss index c594715a2..e35b6494f 100644 --- a/app/assets/stylesheets/left_panel.scss +++ b/app/assets/stylesheets/left_panel.scss @@ -281,3 +281,8 @@ } } } + +.motivation-text-area { + color: #000000; + margin-bottom: 15px; +} diff --git a/app/controllers/backoffice/dossiers_controller.rb b/app/controllers/backoffice/dossiers_controller.rb index d0409acad..e9bb19d90 100644 --- a/app/controllers/backoffice/dossiers_controller.rb +++ b/app/controllers/backoffice/dossiers_controller.rb @@ -117,9 +117,13 @@ class Backoffice::DossiersController < Backoffice::DossiersListController def refuse create_dossier_facade params[:dossier_id] + if params[:dossier] && params[:dossier][:motivation].present? + motivation = params[:dossier][:motivation] + end + dossier = @facade.dossier - dossier.next_step! 'gestionnaire', 'refuse' + dossier.next_step! 'gestionnaire', 'refuse', motivation flash.notice = 'Dossier considéré comme refusé.' NotificationMailer.send_notification(dossier, dossier.procedure.refused_mail_template).deliver_now! @@ -130,9 +134,13 @@ class Backoffice::DossiersController < Backoffice::DossiersListController def without_continuation create_dossier_facade params[:dossier_id] + if params[:dossier] && params[:dossier][:motivation].present? + motivation = params[:dossier][:motivation] + end + dossier = @facade.dossier - dossier.next_step! 'gestionnaire', 'without_continuation' + dossier.next_step! 'gestionnaire', 'without_continuation', motivation flash.notice = 'Dossier considéré comme sans suite.' NotificationMailer.send_notification(dossier, dossier.procedure.without_continuation_mail_template).deliver_now! @@ -143,9 +151,13 @@ class Backoffice::DossiersController < Backoffice::DossiersListController def close create_dossier_facade params[:dossier_id] + if params[:dossier] && params[:dossier][:motivation].present? + motivation = params[:dossier][:motivation] + end + dossier = @facade.dossier - dossier.next_step! 'gestionnaire', 'close' + dossier.next_step! 'gestionnaire', 'close', motivation flash.notice = 'Dossier traité avec succès.' NotificationMailer.send_notification(dossier, dossier.procedure.closed_mail_template).deliver_now! diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 8203618ed..6fbdfb1a2 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -130,7 +130,7 @@ class Dossier < ActiveRecord::Base commentaires.order(created_at: :desc) end - def next_step! role, action + def next_step! role, action, motivation = nil unless %w(initiate follow update comment receive refuse without_continuation close).include?(action) fail 'action is not valid' end @@ -170,14 +170,29 @@ class Dossier < ActiveRecord::Base when 'close' if received? closed! + + if motivation + self.motivation = motivation + save + end end when 'refuse' if received? refused! + + if motivation + self.motivation = motivation + save + end end when 'without_continuation' if received? without_continuation! + + if motivation + self.motivation = motivation + save + end end end end diff --git a/app/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.html.haml b/app/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.html.haml index 24208562b..2acf36723 100644 --- a/app/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.html.haml +++ b/app/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.html.haml @@ -9,6 +9,8 @@ - elsif @facade.dossier.received? = form_tag(backoffice_dossier_process_dossier_url(@facade.dossier.id), method: :post) do + = text_area :dossier, :motivation, class: "motivation-text-area", placeholder: "Motivation (facultative)" + %ul.list-inline %li = button_tag name: :process_action, value: "close", class: 'btn btn-success', title: 'Accepter', data: { toggle: :tooltip, confirm: "Accepter ce dossier ?" } do From 012099553351db6d1a1ea8337cfacf7b4855fe87 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Fri, 2 Jun 2017 10:00:09 +0200 Subject: [PATCH 030/136] [Fix #197] Add the TAG_MOTIVATION email tag --- app/models/concerns/mail_template_concern.rb | 6 ++++++ app/models/mails/closed_mail.rb | 2 +- app/models/mails/refused_mail.rb | 2 +- app/models/mails/without_continuation_mail.rb | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/models/concerns/mail_template_concern.rb b/app/models/concerns/mail_template_concern.rb index 25c87d133..cd44676f5 100644 --- a/app/models/concerns/mail_template_concern.rb +++ b/app/models/concerns/mail_template_concern.rb @@ -21,6 +21,10 @@ module MailTemplateConcern name: "date_de_decision", description: "Permet d'afficher la date à laquelle la décision finale (acceptation, refus, classement sans suite) sur le dossier a été prise." } + TAGS << TAG_MOTIVATION = { + name: "motivation", + description: "Permet d'afficher la motivation associée à la décision finale (acceptation, refus, classement sans suite) sur le dossier. Attention, elle est facultative." + } def object_for_dossier(dossier) replace_tags(object, dossier) @@ -55,6 +59,8 @@ module MailTemplateConcern dossier.procedure.libelle when TAG_DATE_DE_DECISION dossier.processed_at.present? ? dossier.processed_at.localtime.strftime("%d/%m/%Y") : "" + when TAG_MOTIVATION + dossier.motivation || "" else '--BALISE_NON_RECONNUE--' end diff --git a/app/models/mails/closed_mail.rb b/app/models/mails/closed_mail.rb index 80f706bf9..cef3a274f 100644 --- a/app/models/mails/closed_mail.rb +++ b/app/models/mails/closed_mail.rb @@ -6,6 +6,6 @@ module Mails TEMPLATE_NAME = "mails/closed_mail" DISPLAYED_NAME = "Accusé d'acceptation" DEFAULT_OBJECT = 'Votre dossier TPS nº --numero_dossier-- a été accepté' - ALLOWED_TAGS = [TAG_NUMERO_DOSSIER, TAG_LIEN_DOSSIER, TAG_LIBELLE_PROCEDURE, TAG_DATE_DE_DECISION] + ALLOWED_TAGS = [TAG_NUMERO_DOSSIER, TAG_LIEN_DOSSIER, TAG_LIBELLE_PROCEDURE, TAG_DATE_DE_DECISION, TAG_MOTIVATION] end end diff --git a/app/models/mails/refused_mail.rb b/app/models/mails/refused_mail.rb index f9b859e74..8d0b9d032 100644 --- a/app/models/mails/refused_mail.rb +++ b/app/models/mails/refused_mail.rb @@ -6,6 +6,6 @@ module Mails TEMPLATE_NAME = "mails/refused_mail" DISPLAYED_NAME = 'Accusé de rejet du dossier' DEFAULT_OBJECT = 'Votre dossier TPS nº --numero_dossier-- a été refusé' - ALLOWED_TAGS = [TAG_NUMERO_DOSSIER, TAG_LIEN_DOSSIER, TAG_LIBELLE_PROCEDURE, TAG_DATE_DE_DECISION] + ALLOWED_TAGS = [TAG_NUMERO_DOSSIER, TAG_LIEN_DOSSIER, TAG_LIBELLE_PROCEDURE, TAG_DATE_DE_DECISION, TAG_MOTIVATION] end end diff --git a/app/models/mails/without_continuation_mail.rb b/app/models/mails/without_continuation_mail.rb index 0115037c3..7e615ca75 100644 --- a/app/models/mails/without_continuation_mail.rb +++ b/app/models/mails/without_continuation_mail.rb @@ -6,6 +6,6 @@ module Mails TEMPLATE_NAME = "mails/without_continuation_mail" DISPLAYED_NAME = 'Accusé de classement sans suite' DEFAULT_OBJECT = 'Votre dossier TPS nº --numero_dossier-- a été classé sans suite' - ALLOWED_TAGS = [TAG_NUMERO_DOSSIER, TAG_LIEN_DOSSIER, TAG_LIBELLE_PROCEDURE, TAG_DATE_DE_DECISION] + ALLOWED_TAGS = [TAG_NUMERO_DOSSIER, TAG_LIEN_DOSSIER, TAG_LIBELLE_PROCEDURE, TAG_DATE_DE_DECISION, TAG_MOTIVATION] end end From 12d74be668b125d7c52018ecf39fabd14148ab48 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Wed, 7 Jun 2017 11:42:14 +0200 Subject: [PATCH 031/136] Refactor Backoffice::DossiersController#process_dossier --- .../backoffice/dossiers_controller.rb | 70 +++----- .../backoffice/dossiers_controller_spec.rb | 159 ++++++++---------- 2 files changed, 86 insertions(+), 143 deletions(-) diff --git a/app/controllers/backoffice/dossiers_controller.rb b/app/controllers/backoffice/dossiers_controller.rb index e9bb19d90..a5477430f 100644 --- a/app/controllers/backoffice/dossiers_controller.rb +++ b/app/controllers/backoffice/dossiers_controller.rb @@ -104,63 +104,33 @@ class Backoffice::DossiersController < Backoffice::DossiersListController end def process_dossier + create_dossier_facade params[:dossier_id] + + if params[:dossier] && params[:dossier][:motivation].present? + motivation = params[:dossier][:motivation] + end + + dossier = @facade.dossier + case params[:process_action] when "refuse" - refuse + next_step = "refuse" + notice = "Dossier considéré comme refusé." + template = dossier.procedure.refused_mail_template when "without_continuation" - without_continuation + next_step = "without_continuation" + notice = "Dossier considéré comme sans suite." + template = dossier.procedure.without_continuation_mail_template when "close" - close - end - end - - def refuse - create_dossier_facade params[:dossier_id] - - if params[:dossier] && params[:dossier][:motivation].present? - motivation = params[:dossier][:motivation] + next_step = "close" + notice = "Dossier traité avec succès." + template = dossier.procedure.closed_mail_template end - dossier = @facade.dossier + dossier.next_step! 'gestionnaire', next_step, motivation + flash.notice = notice - dossier.next_step! 'gestionnaire', 'refuse', motivation - flash.notice = 'Dossier considéré comme refusé.' - - NotificationMailer.send_notification(dossier, dossier.procedure.refused_mail_template).deliver_now! - - redirect_to backoffice_dossier_path(id: dossier.id) - end - - def without_continuation - create_dossier_facade params[:dossier_id] - - if params[:dossier] && params[:dossier][:motivation].present? - motivation = params[:dossier][:motivation] - end - - dossier = @facade.dossier - - dossier.next_step! 'gestionnaire', 'without_continuation', motivation - flash.notice = 'Dossier considéré comme sans suite.' - - NotificationMailer.send_notification(dossier, dossier.procedure.without_continuation_mail_template).deliver_now! - - redirect_to backoffice_dossier_path(id: dossier.id) - end - - def close - create_dossier_facade params[:dossier_id] - - if params[:dossier] && params[:dossier][:motivation].present? - motivation = params[:dossier][:motivation] - end - - dossier = @facade.dossier - - dossier.next_step! 'gestionnaire', 'close', motivation - flash.notice = 'Dossier traité avec succès.' - - NotificationMailer.send_notification(dossier, dossier.procedure.closed_mail_template).deliver_now! + NotificationMailer.send_notification(dossier, template).deliver_now! redirect_to backoffice_dossier_path(id: dossier.id) end diff --git a/spec/controllers/backoffice/dossiers_controller_spec.rb b/spec/controllers/backoffice/dossiers_controller_spec.rb index 2edff6017..5d11f4b2d 100644 --- a/spec/controllers/backoffice/dossiers_controller_spec.rb +++ b/spec/controllers/backoffice/dossiers_controller_spec.rb @@ -251,112 +251,85 @@ describe Backoffice::DossiersController, type: :controller do end describe 'POST #process_dossier' do - before do - dossier.received! - sign_in gestionnaire - end - context "with refuse" do - it "calls the refuse method" do - expect(controller).to receive(:refuse) - - post :process_dossier, params: { dossier_id: dossier_id, process_action: "refuse" } + before do + dossier.received! + sign_in gestionnaire end + + subject { post :process_dossier, params: { process_action: "refuse", dossier_id: dossier_id} } + + it 'change state to refused' do + subject + + dossier.reload + expect(dossier.state).to eq('refused') + end + + it 'Notification email is sent' do + expect(NotificationMailer).to receive(:send_notification) + .with(dossier, kind_of(Mails::RefusedMail)).and_return(NotificationMailer) + expect(NotificationMailer).to receive(:deliver_now!) + + subject + end + + it { is_expected.to redirect_to backoffice_dossier_path(id: dossier.id) } end context "with without_continuation" do - it "calls the without_continuation method" do - expect(controller).to receive(:without_continuation) - - post :process_dossier, params: { dossier_id: dossier_id, process_action: "without_continuation" } + before do + dossier.received! + sign_in gestionnaire end + + subject { post :process_dossier, params: { process_action: "without_continuation", dossier_id: dossier_id} } + + it 'change state to without_continuation' do + subject + + dossier.reload + expect(dossier.state).to eq('without_continuation') + end + + it 'Notification email is sent' do + expect(NotificationMailer).to receive(:send_notification) + .with(dossier, kind_of(Mails::WithoutContinuationMail)).and_return(NotificationMailer) + expect(NotificationMailer).to receive(:deliver_now!) + + subject + end + + it { is_expected.to redirect_to backoffice_dossier_path(id: dossier.id) } end context "with close" do - it "calls the close method" do - expect(controller).to receive(:close) - - post :process_dossier, params: { dossier_id: dossier_id, process_action: "close" } + before do + dossier.received! + sign_in gestionnaire end + + subject { post :process_dossier, params: { process_action: "close", dossier_id: dossier_id} } + + it 'change state to closed' do + subject + + dossier.reload + expect(dossier.state).to eq('closed') + end + + it 'Notification email is sent' do + expect(NotificationMailer).to receive(:send_notification) + .with(dossier, kind_of(Mails::ClosedMail)).and_return(NotificationMailer) + expect(NotificationMailer).to receive(:deliver_now!) + + subject + end + + it { is_expected.to redirect_to backoffice_dossier_path(id: dossier.id) } end end - describe 'POST #refuse' do - before do - dossier.received! - sign_in gestionnaire - end - - subject { post :refuse, params: {dossier_id: dossier_id} } - - it 'change state to refused' do - subject - - dossier.reload - expect(dossier.state).to eq('refused') - end - - it 'Notification email is sent' do - expect(NotificationMailer).to receive(:send_notification) - .with(dossier, kind_of(Mails::RefusedMail)).and_return(NotificationMailer) - expect(NotificationMailer).to receive(:deliver_now!) - - subject - end - - it { is_expected.to redirect_to backoffice_dossier_path(id: dossier.id) } - end - - describe 'POST #without_continuation' do - before do - dossier.received! - sign_in gestionnaire - end - subject { post :without_continuation, params: {dossier_id: dossier_id} } - - it 'change state to without_continuation' do - subject - - dossier.reload - expect(dossier.state).to eq('without_continuation') - end - - it 'Notification email is sent' do - expect(NotificationMailer).to receive(:send_notification) - .with(dossier, kind_of(Mails::WithoutContinuationMail)).and_return(NotificationMailer) - expect(NotificationMailer).to receive(:deliver_now!) - - subject - end - - it { is_expected.to redirect_to backoffice_dossier_path(id: dossier.id) } - end - - describe 'POST #close' do - before do - dossier.received! - sign_in gestionnaire - end - subject { post :close, params: {dossier_id: dossier_id} } - - it 'change state to closed' do - subject - - dossier.reload - expect(dossier.state).to eq('closed') - end - - it 'Notification email is sent' do - expect(NotificationMailer).to receive(:send_notification) - .with(dossier, kind_of(Mails::ClosedMail)).and_return(NotificationMailer) - expect(NotificationMailer).to receive(:deliver_now!) - - subject - end - - it { is_expected.to redirect_to backoffice_dossier_path(id: dossier.id) } - end - describe 'PUT #toggle_follow' do before do sign_in gestionnaire From b8798d8b7080c1f1ca06dfdd9c500382e4a3b461 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Wed, 7 Jun 2017 11:42:25 +0200 Subject: [PATCH 032/136] Remove now unused routes --- config/routes.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index 00cd47949..c09431946 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -173,9 +173,6 @@ Rails.application.routes.draw do resources :dossiers do post 'receive' => 'dossiers#receive' post 'process_dossier' => 'dossiers#process_dossier' - post 'refuse' => 'dossiers#refuse' - post 'without_continuation' => 'dossiers#without_continuation' - post 'close' => 'dossiers#close' member do post 'archive' post 'unarchive' From b64c6d88171935abb2027e64b5495693073048ec Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 13 Jun 2017 14:59:57 +0200 Subject: [PATCH 033/136] =?UTF-8?q?Add=20a=20separator=20before=20the=20?= =?UTF-8?q?=E2=80=9CReouvrir=E2=80=9D=20button?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_left_panel_backoffice_dossierscontroller_show.html.haml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.html.haml b/app/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.html.haml index 2acf36723..c24112ece 100644 --- a/app/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.html.haml +++ b/app/views/layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.html.haml @@ -24,6 +24,7 @@ = button_tag name: :process_action, value: "refuse", class: 'btn btn-danger', title: 'Refuser', data: { toggle: :tooltip, confirm: "Refuser ce dossier ?" } do %i.fa.fa-times + %hr = link_to 'Reouvrir', backoffice_dossier_reopen_path(@facade.dossier), method: :post, class: 'btn btn-default btn-block', data: { confirm: "Confirmer vous la réouverture de ce dossier ?" } %hr From 5855c2552af1560872a36abd41f85691c5602cdb Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Wed, 24 May 2017 15:13:45 +0200 Subject: [PATCH 034/136] [Fix #196] Config: install prawn --- Gemfile | 4 ++++ Gemfile.lock | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/Gemfile b/Gemfile index ea875153b..fce678101 100644 --- a/Gemfile +++ b/Gemfile @@ -101,6 +101,10 @@ gem 'sinatra', git: 'https://github.com/sinatra/sinatra.git', require: false gem 'select2-rails' +# PDF Generation +gem 'prawn', '~> 2.0.1' +gem 'prawn_rails', '~> 0.0.11' + group :test do gem 'capybara' gem 'launchy' diff --git a/Gemfile.lock b/Gemfile.lock index 5981b90c6..9a6646416 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -420,12 +420,19 @@ GEM parallel (1.11.2) parser (2.4.0.0) ast (~> 2.2) + pdf-core (0.6.1) pg (0.19.0) poltergeist (1.14.0) capybara (~> 2.1) cliver (~> 0.3.1) websocket-driver (>= 0.2.0) powerpack (0.1.1) + prawn (2.0.2) + pdf-core (~> 0.6.0) + ttfunk (~> 1.4.0) + prawn_rails (0.0.11) + prawn (>= 0.11.1) + railties (>= 3.0.0) pry (0.10.4) coderay (~> 1.1.0) method_source (~> 0.8.1) @@ -611,6 +618,7 @@ GEM tilt (2.0.5) timecop (0.8.1) trollop (2.1.2) + ttfunk (1.4.0) turbolinks (5.0.1) turbolinks-source (~> 5) turbolinks-source (5.0.0) @@ -706,6 +714,8 @@ DEPENDENCIES openstack pg poltergeist + prawn (~> 2.0.1) + prawn_rails (~> 0.0.11) pry-byebug rack-handlers rails (= 5.0.0.1) From d7ff3d61392548565affcb09c4a513cfc9f2cc62 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Thu, 8 Jun 2017 14:16:48 +0200 Subject: [PATCH 035/136] [Fix #196] AttestationTemplate: add model --- Gemfile | 1 + Gemfile.lock | 3 + app/models/attestation_template.rb | 40 +++++++++ app/models/procedure.rb | 2 + .../attestation_template_image_uploader.rb | 26 ++++++ ...0524140630_create_attestation_templates.rb | 16 ++++ db/schema.rb | 17 +++- spec/factories/attestation_template.rb | 8 ++ spec/fixtures/black.png | Bin 0 -> 67 bytes spec/fixtures/white.png | Bin 0 -> 67 bytes spec/models/attestation_template_spec.rb | 76 ++++++++++++++++++ spec/models/procedure_spec.rb | 13 +++ 12 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 app/models/attestation_template.rb create mode 100644 app/uploaders/attestation_template_image_uploader.rb create mode 100644 db/migrate/20170524140630_create_attestation_templates.rb create mode 100644 spec/factories/attestation_template.rb create mode 100644 spec/fixtures/black.png create mode 100644 spec/fixtures/white.png create mode 100644 spec/models/attestation_template_spec.rb diff --git a/Gemfile b/Gemfile index fce678101..c6e525175 100644 --- a/Gemfile +++ b/Gemfile @@ -52,6 +52,7 @@ gem 'rest-client' gem 'clamav-client', require: 'clamav/client' gem 'carrierwave' +gem 'copy_carrierwave_file' gem 'fog' gem 'fog-openstack' diff --git a/Gemfile.lock b/Gemfile.lock index 9a6646416..5ff8a870a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -120,6 +120,8 @@ GEM coffee-script-source (1.12.2) concurrent-ruby (1.0.5) connection_pool (2.2.1) + copy_carrierwave_file (1.3.0) + carrierwave (>= 0.9) crack (0.4.3) safe_yaml (~> 1.0.0) database_cleaner (1.5.3) @@ -685,6 +687,7 @@ DEPENDENCIES carrierwave chartkick clamav-client + copy_carrierwave_file database_cleaner deep_cloneable (~> 2.2.1) devise diff --git a/app/models/attestation_template.rb b/app/models/attestation_template.rb new file mode 100644 index 000000000..fd3ca4575 --- /dev/null +++ b/app/models/attestation_template.rb @@ -0,0 +1,40 @@ +class AttestationTemplate < ApplicationRecord + include ActionView::Helpers::NumberHelper + + belongs_to :procedure + + mount_uploader :logo, AttestationTemplateImageUploader + mount_uploader :signature, AttestationTemplateImageUploader + + validate :logo_signature_file_size + + FILE_MAX_SIZE_IN_MB = 0.5 + + def dup + result = AttestationTemplate.new(title: title, body: body, footer: footer, activated: activated) + + if logo.present? + CopyCarrierwaveFile::CopyFileService.new(self, result, :logo).set_file + end + + if signature.present? + CopyCarrierwaveFile::CopyFileService.new(self, result, :signature).set_file + end + + result + end + + private + + def logo_signature_file_size + %i[logo signature] + .select { |file_name| send(file_name).present? } + .each { |file_name| file_size_check(file_name) } + end + + def file_size_check(file_name) + if send(file_name).file.size.to_f > FILE_MAX_SIZE_IN_MB.megabyte.to_f + errors.add(file_name, " : vous ne pouvez pas charger une image de plus de #{number_with_delimiter(FILE_MAX_SIZE_IN_MB, locale: :fr)} Mo") + end + end +end diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 99d44c77e..38c7200e3 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -7,6 +7,7 @@ class Procedure < ActiveRecord::Base has_one :procedure_path, dependent: :destroy has_one :module_api_carto, dependent: :destroy + has_one :attestation_template, dependent: :destroy belongs_to :administrateur @@ -93,6 +94,7 @@ class Procedure < ActiveRecord::Base :types_de_champ, :types_de_champ_private, :module_api_carto, + :attestation_template, types_de_champ: [:drop_down_list] ]) procedure.archived = false diff --git a/app/uploaders/attestation_template_image_uploader.rb b/app/uploaders/attestation_template_image_uploader.rb new file mode 100644 index 000000000..4baa5fe54 --- /dev/null +++ b/app/uploaders/attestation_template_image_uploader.rb @@ -0,0 +1,26 @@ +class AttestationTemplateImageUploader < BaseUploader + def root + File.join(Rails.root, 'public') + end + + # Choose what kind of storage to use for this uploader: + if Features.remote_storage + storage :fog + else + storage :file + end + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: + def store_dir + unless Features.remote_storage + "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" + end + end + + # Add a white list of extensions which are allowed to be uploaded. + # For images you might use something like this: + def extension_white_list + %w(jpg jpeg png) + end +end diff --git a/db/migrate/20170524140630_create_attestation_templates.rb b/db/migrate/20170524140630_create_attestation_templates.rb new file mode 100644 index 000000000..6517c72ea --- /dev/null +++ b/db/migrate/20170524140630_create_attestation_templates.rb @@ -0,0 +1,16 @@ +class CreateAttestationTemplates < ActiveRecord::Migration[5.0] + def change + create_table :attestation_templates do |t| + t.text :title + t.text :body + t.text :footer + t.string :logo + t.string :signature + t.boolean :activated + + t.timestamps + + t.references :procedure, index: { unique: true }, foreign_key: true + end + end +end diff --git a/db/schema.rb b/db/schema.rb index b24616cc2..10d5778c5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,8 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170530141608) do +ActiveRecord::Schema.define(version: 20170524140630) do + # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -70,6 +71,19 @@ ActiveRecord::Schema.define(version: 20170530141608) do t.index ["procedure_id"], name: "index_assign_tos_on_procedure_id", using: :btree end + create_table "attestation_templates", force: :cascade do |t| + t.text "title" + t.text "body" + t.text "footer" + t.string "logo" + t.string "signature" + t.boolean "activated" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "procedure_id" + t.index ["procedure_id"], name: "index_attestation_templates_on_procedure_id", unique: true, using: :btree + end + create_table "avis", force: :cascade do |t| t.string "email" t.text "introduction" @@ -448,6 +462,7 @@ ActiveRecord::Schema.define(version: 20170530141608) do t.index ["procedure_id"], name: "index_without_continuation_mails_on_procedure_id", using: :btree end + add_foreign_key "attestation_templates", "procedures" add_foreign_key "avis", "gestionnaires", column: "claimant_id" add_foreign_key "cerfas", "dossiers" add_foreign_key "closed_mails", "procedures" diff --git a/spec/factories/attestation_template.rb b/spec/factories/attestation_template.rb new file mode 100644 index 000000000..46eb4bee1 --- /dev/null +++ b/spec/factories/attestation_template.rb @@ -0,0 +1,8 @@ +FactoryGirl.define do + factory :attestation_template do + title 'title' + body 'body' + footer 'footer' + activated true + end +end diff --git a/spec/fixtures/black.png b/spec/fixtures/black.png new file mode 100644 index 0000000000000000000000000000000000000000..17f1d2ed7807fce394675e37973e22ee9928be0e GIT binary patch literal 67 zcmeAS@N?(olHy`uVBq!ia0vp^j3CSbBp9sfW`_bPE>9Q7kcv6UNkBFm1GAZV%?gmL Mr>mdKI;Vst09<9Q7kcv6UzxWv#Ss9tmFVH&+ OlJ#`;b6Mw<&;$T@!wsPT literal 0 HcmV?d00001 diff --git a/spec/models/attestation_template_spec.rb b/spec/models/attestation_template_spec.rb new file mode 100644 index 000000000..f5d7c62bb --- /dev/null +++ b/spec/models/attestation_template_spec.rb @@ -0,0 +1,76 @@ +describe AttestationTemplate, type: :model do + describe 'validate' do + let(:logo_size) { AttestationTemplate::FILE_MAX_SIZE_IN_MB.megabyte } + let(:signature_size) { AttestationTemplate::FILE_MAX_SIZE_IN_MB.megabyte } + let(:fake_logo) { double(AttestationTemplateImageUploader, file: double(size: logo_size)) } + let(:fake_signature) { double(AttestationTemplateImageUploader, file: double(size: signature_size)) } + let(:attestation_template) { AttestationTemplate.new } + + before do + allow(attestation_template).to receive(:logo).and_return(fake_logo) + allow(attestation_template).to receive(:signature).and_return(fake_signature) + attestation_template.validate + end + + subject { attestation_template.errors.details } + + context 'when no files are present' do + let(:fake_logo) { nil } + let(:fake_signature) { nil } + + it { is_expected.to match({}) } + end + + context 'when the logo and the signature have the right size' do + it { is_expected.to match({}) } + end + + context 'when the logo and the signature are too heavy' do + let(:logo_size) { AttestationTemplate::FILE_MAX_SIZE_IN_MB.megabyte + 1 } + let(:signature_size) { AttestationTemplate::FILE_MAX_SIZE_IN_MB.megabyte + 1 } + + it do + expected = { + signature: [{ error: ' : vous ne pouvez pas charger une image de plus de 0,5 Mo' }], + logo: [{ error: ' : vous ne pouvez pas charger une image de plus de 0,5 Mo' }] + } + + is_expected.to match(expected) + end + end + end + + describe 'dup' do + before do + @logo = File.open('spec/fixtures/white.png') + @signature = File.open('spec/fixtures/black.png') + end + + after do + @logo.close + @signature.close + subject.destroy + end + + let(:attestation_template) { AttestationTemplate.create(attributes) } + subject { attestation_template.dup } + + context 'with an attestation without images' do + let(:attributes) { { title: 't', body: 'b', footer: 'f', activated: true } } + + it { is_expected.to have_attributes(attributes) } + it { is_expected.to have_attributes(id: nil) } + it { expect(subject.logo.file).to be_nil } + end + + context 'with an attestation with images' do + let(:attributes) { { logo: @logo, signature: @signature } } + + it { expect(subject.logo.file.file).not_to eq(attestation_template.logo.file.file) } + it { expect(subject.logo.file.read).to eq(attestation_template.logo.file.read) } + + it { expect(subject.signature.file.file).not_to eq(attestation_template.signature.file.file) } + it { expect(subject.signature.file.read).to eq(attestation_template.signature.file.read) } + end + end +end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 067ca981e..d0f36ecea 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -153,6 +153,17 @@ describe Procedure do let!(:piece_justificative_1) { create(:type_de_piece_justificative, procedure: procedure, order_place: 1) } let(:received_mail){ create(:received_mail) } + before do + @logo = File.open('spec/fixtures/white.png') + @signature = File.open('spec/fixtures/black.png') + @attestation_template = create(:attestation_template, procedure: procedure, logo: @logo, signature: @signature) + end + + after do + @logo.close + @signature.close + end + subject { procedure.clone } it 'should duplicate specific objects with different id' do @@ -175,6 +186,8 @@ describe Procedure do subject.types_de_piece_justificative.zip(procedure.types_de_piece_justificative).each do |stc, ptc| expect(stc).to have_same_attributes_as(ptc) end + + expect(subject.attestation_template.title).to eq(procedure.attestation_template.title) end it 'should duplicate existing mail_templates' do From fdb4d15bcf6b44fd85c64a5c2536d809ab826fe6 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Fri, 9 Jun 2017 13:28:47 +0200 Subject: [PATCH 036/136] [Fix #196] AttestationTemplate: add edition --- app/assets/stylesheets/application.scss | 1 + .../attestation_template_edit.scss | 30 +++++ .../admin/attestation_templates_controller.rb | 49 ++++++++ app/models/attestation_template.rb | 36 ++++++ .../attestation_templates/edit.html.haml | 71 +++++++++++ ...estationtemplatescontroller_edit.html.haml | 1 + ...dmin_procedurescontroller_navbar.html.haml | 5 + config/routes.rb | 5 + .../attestation_templates_controller_spec.rb | 119 ++++++++++++++++++ 9 files changed, 317 insertions(+) create mode 100644 app/assets/stylesheets/attestation_template_edit.scss create mode 100644 app/controllers/admin/attestation_templates_controller.rb create mode 100644 app/views/admin/attestation_templates/edit.html.haml create mode 100644 app/views/layouts/left_panels/_left_panel_admin_attestationtemplatescontroller_edit.html.haml create mode 100644 spec/controllers/admin/attestation_templates_controller_spec.rb diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index aac24c497..5368690e7 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -44,6 +44,7 @@ // = require switch_menu // = require typeahead // = require users +// = require attestation_template_edit // = require_self // = require bootstrap-datepicker3 diff --git a/app/assets/stylesheets/attestation_template_edit.scss b/app/assets/stylesheets/attestation_template_edit.scss new file mode 100644 index 000000000..f6105a8f6 --- /dev/null +++ b/app/assets/stylesheets/attestation_template_edit.scss @@ -0,0 +1,30 @@ +#attestation-template-edit { + .notice { + margin-bottom: 30px; + } + + .image-upload { + display: flex; + align-items: center; + + input { + margin: 10px 0; + } + } + + .thumbnail { + width: 90px; + margin-right: 15px; + } + + .balises { + max-height: 180px; + overflow-y: scroll; + margin-bottom: 20px; + } + + .table { + border: 1px solid #DDDDDD; + margin-bottom: 0px; + } +} diff --git a/app/controllers/admin/attestation_templates_controller.rb b/app/controllers/admin/attestation_templates_controller.rb new file mode 100644 index 000000000..ca96f235e --- /dev/null +++ b/app/controllers/admin/attestation_templates_controller.rb @@ -0,0 +1,49 @@ +class Admin::AttestationTemplatesController < AdminController + before_action :retrieve_procedure + + def edit + @attestation_template = @procedure.attestation_template || AttestationTemplate.new(procedure: @procedure) + end + + def update + attestation_template = @procedure.attestation_template + + if attestation_template.update_attributes(activated_attestation_params) + flash.notice = "L'attestation a bien été modifiée" + else + flash.alert = attestation_template.errors.full_messages.join('
') + end + + redirect_to edit_admin_procedure_attestation_template_path(@procedure) + end + + def create + attestation_template = AttestationTemplate.new(activated_attestation_params.merge(procedure_id: @procedure.id)) + + if attestation_template.save + flash.notice = "L'attestation a bien été sauvegardée" + else + flash.alert = attestation_template.errors.full_messages.join('
') + end + + redirect_to edit_admin_procedure_attestation_template_path(@procedure) + end + + def disactivate + attestation_template = @procedure.attestation_template + attestation_template.activated = false + attestation_template.save + + flash.notice = "L'attestation a bien été désactivée" + + redirect_to edit_admin_procedure_attestation_template_path(@procedure) + end + + private + + def activated_attestation_params + params.require(:attestation_template) + .permit(:title, :body, :footer, :logo, :signature) + .merge(activated: true) + end +end diff --git a/app/models/attestation_template.rb b/app/models/attestation_template.rb index fd3ca4575..cd625e39d 100644 --- a/app/models/attestation_template.rb +++ b/app/models/attestation_template.rb @@ -10,6 +10,16 @@ class AttestationTemplate < ApplicationRecord FILE_MAX_SIZE_IN_MB = 0.5 + def tags + if procedure.for_individual? + identity_tags = individual_tags + else + identity_tags = entreprise_tags + etablissement_tags + end + + identity_tags + dossier_tags + procedure_type_de_champ_public_private_tags + end + def dup result = AttestationTemplate.new(title: title, body: body, footer: footer, activated: activated) @@ -37,4 +47,30 @@ class AttestationTemplate < ApplicationRecord errors.add(file_name, " : vous ne pouvez pas charger une image de plus de #{number_with_delimiter(FILE_MAX_SIZE_IN_MB, locale: :fr)} Mo") end end + + def procedure_type_de_champ_public_private_tags + (procedure.types_de_champ + procedure.types_de_champ_private) + .map { |tdc| { libelle: tdc.libelle, description: tdc.description } } + end + + def dossier_tags + [{ libelle: 'motivation', description: '', target: 'motivation' }] + end + + def individual_tags + [{ libelle: 'civilité', description: 'M., Mme' }, + { libelle: 'nom', description: "nom de l'usager" }, + { libelle: 'prénom', description: "prénom de l'usager" }] + end + + def entreprise_tags + [{ libelle: 'SIREN', description: '' }, + { libelle: 'numéro de TVA intracommunautaire', description: '' }, + { libelle: 'SIRET du siège social', description: '' }, + { libelle: 'raison sociale', description: '' }] + end + + def etablissement_tags + [{ libelle: 'adresse', description: '' }] + end end diff --git a/app/views/admin/attestation_templates/edit.html.haml b/app/views/admin/attestation_templates/edit.html.haml new file mode 100644 index 000000000..9b3069919 --- /dev/null +++ b/app/views/admin/attestation_templates/edit.html.haml @@ -0,0 +1,71 @@ +#attestation-template-edit.row.white-back + = form_for @attestation_template, url: admin_procedure_attestation_template_path do |f| + .row + .col-md-10 + %h1 + Attestation + - if @attestation_template.activated? + %small.text-success Activée + - else + %small Désactivée + + %p.notice Les attestation, si elles sont activées, sont délivrées par email aux usagers lorsque leurs dossiers sont acceptés, et sont également disponibles au téléchargement sur leur espace personnel. + + .image-upload + - if @attestation_template.logo.present? + = image_tag @attestation_template.logo.url, class: 'thumbnail' + .form-group + = f.label :logo, "Logo de l'attestation" + = f.file_field :logo, accept: 'image/png, image/jpg, image/jpeg' + %p.help-block + Fichier accepté : JPG / JPEG / PNG + %br + Dimensions conseillées : au minimum 500 px de largeur ou de hauteur, poids maximum : 0,5 Mo. + + .form-group + = f.label :title, 'Titre' + = f.text_field :title, class: 'form-control' + + .form-group + = f.label :body, 'Corps du document' + ~ f.text_area :body, class: 'form-control', rows: 10 + + .row + .col-md-12.balises + %table.table.table-striped + %tr + %th.col-md-3 + Balise + %th + Description + - @attestation_template.tags.each do |tag| + %tr + %td + = "--#{tag[:libelle]}--" + %td + = tag[:description] + + .image-upload + - if @attestation_template.signature.present? + = image_tag @attestation_template.signature.url, class: 'thumbnail' + .form-group + = f.label :signature, "Tampon de l'attestation" + = f.file_field :signature, accept: 'image/png, image/jpg, image/jpeg' + %p.help-block + Fichier accepté : JPG / JPEG / PNG + %br + Dimensions conseillées : au minimum 500 px de largeur ou de hauteur, poids maximum : 0,5 Mo. + + .form-group + = f.label :footer, 'Adresse en bas de page' + ~ f.text_area :footer, class: 'form-control', rows: 2, + placeholder: "Direction interministérielle du numérique et du système d'information et de communication de l'Etat (DINSIC)" + + .pull-right + - if @attestation_template.activated + %button.btn.btn-warning{ formaction: admin_procedure_attestation_template_disactivate_path } désactiver l'attestation + + - if @attestation_template.id.nil? || !@attestation_template.activated + %button.btn.btn-success Activer l'attestation + - else + %button.btn.btn-success Enregistrer diff --git a/app/views/layouts/left_panels/_left_panel_admin_attestationtemplatescontroller_edit.html.haml b/app/views/layouts/left_panels/_left_panel_admin_attestationtemplatescontroller_edit.html.haml new file mode 100644 index 000000000..832761229 --- /dev/null +++ b/app/views/layouts/left_panels/_left_panel_admin_attestationtemplatescontroller_edit.html.haml @@ -0,0 +1 @@ += render partial: 'layouts/left_panels/left_panel_admin_procedurescontroller_navbar', locals: { active: 'Attestation' } diff --git a/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml b/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml index 0bb60d458..60ee10888 100644 --- a/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml +++ b/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml @@ -45,6 +45,11 @@ .procedure-list-element{ class: ('active' if active == 'Prévisualisation') } Prévisualisation + - unless @procedure.locked? + %a#onglet-attestation{ href: url_for(edit_admin_procedure_attestation_template_path(@procedure)) } + .procedure-list-element{ class: ('active' if active == 'Attestation') } + Attestation + .split-hr-left diff --git a/config/routes.rb b/config/routes.rb index c09431946..0a2075ca8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -143,6 +143,11 @@ Rails.application.routes.draw do resource :accompagnateurs, only: [:show, :update] resource :previsualisation, only: [:show] + + resource :attestation_template, only: [:edit, :update, :create] + + post 'attestation_template/disactivate' => 'attestation_templates#disactivate' + patch 'attestation_template/disactivate' => 'attestation_templates#disactivate' end namespace :accompagnateurs do diff --git a/spec/controllers/admin/attestation_templates_controller_spec.rb b/spec/controllers/admin/attestation_templates_controller_spec.rb new file mode 100644 index 000000000..7f40c6ecc --- /dev/null +++ b/spec/controllers/admin/attestation_templates_controller_spec.rb @@ -0,0 +1,119 @@ +describe Admin::AttestationTemplatesController, type: :controller do + let!(:attestation_template) { create(:attestation_template) } + let(:admin) { create(:administrateur) } + let!(:procedure) { create :procedure, administrateur: admin, attestation_template: attestation_template } + let(:logo) { fixture_file_upload('spec/fixtures/white.png', 'image/png') } + let(:signature) { fixture_file_upload('spec/fixtures/black.png', 'image/png') } + + before do + sign_in admin + end + + describe 'GET #edit' do + before { get :edit, params: { procedure_id: procedure.id } } + + context 'if an attestation template exists on the procedure' do + it { expect(subject.status).to eq(200) } + it { expect(assigns(:attestation_template)).to eq(attestation_template) } + end + + context 'if an attestation template does not exist on the procedure' do + let(:attestation_template) { nil } + it { expect(subject.status).to eq(200) } + it { expect(assigns(:attestation_template).id).to be_nil } + it { expect(assigns(:attestation_template)).to be_an_instance_of(AttestationTemplate) } + end + end + + describe 'POST #create' do + let(:attestation_template) { nil } + let(:attestation_params) { { title: 't', body: 'b', footer: 'f' } } + + context 'nominal' do + before do + post :create, + params: { procedure_id: procedure.id, + attestation_template: attestation_params.merge(logo: logo, signature: signature) } + procedure.reload + end + + it { expect(procedure.attestation_template).to have_attributes(attestation_params) } + it { expect(procedure.attestation_template.activated).to be true } + it { expect(procedure.attestation_template.logo.read).to eq(logo.read) } + it { expect(procedure.attestation_template.signature.read).to eq(signature.read) } + it { expect(response).to redirect_to edit_admin_procedure_attestation_template_path(procedure) } + it { expect(flash.notice).to eq("L'attestation a bien été sauvegardée") } + + after { procedure.attestation_template.destroy } + end + + context 'when something wrong happens in the attestation template creation' do + before do + expect_any_instance_of(AttestationTemplate).to receive(:save) + .and_return(false) + expect_any_instance_of(AttestationTemplate).to receive(:errors) + .and_return(double(full_messages: ['nop'])) + + post :create, + params: { procedure_id: procedure.id, + attestation_template: attestation_params } + procedure.reload + end + + it { expect(response).to redirect_to edit_admin_procedure_attestation_template_path(procedure) } + it { expect(flash.alert).to eq('nop') } + end + end + + describe 'PATCH #update' do + let(:attestation_params) { { title: 't', body: 'b', footer: 'f' } } + let(:attestation_params_with_images) { attestation_params.merge(logo: logo, signature: signature) } + + context 'nominal' do + before do + patch :update, + params: { procedure_id: procedure.id, + attestation_template: attestation_params_with_images } + procedure.reload + end + + it { expect(procedure.attestation_template).to have_attributes(attestation_params) } + it { expect(procedure.attestation_template.logo.read).to eq(logo.read) } + it { expect(procedure.attestation_template.signature.read).to eq(signature.read) } + it { expect(response).to redirect_to edit_admin_procedure_attestation_template_path(procedure) } + it { expect(flash.notice).to eq("L'attestation a bien été modifiée") } + + after { procedure.attestation_template.destroy } + end + + context 'when something wrong happens in the attestation template creation' do + before do + expect_any_instance_of(AttestationTemplate).to receive(:update_attributes).and_return(false) + expect_any_instance_of(AttestationTemplate).to receive(:errors) + .and_return(double(full_messages: ['nop'])) + + patch :update, + params: { procedure_id: procedure.id, + attestation_template: attestation_params_with_images } + procedure.reload + end + + it { expect(response).to redirect_to edit_admin_procedure_attestation_template_path(procedure) } + it { expect(flash.alert).to eq('nop') } + end + end + + describe 'post #disactivate' do + context 'when the attestation_template is activated' do + let(:attestation_template) { create(:attestation_template, activated: true) } + + before do + post :disactivate, params: { procedure_id: procedure.id } + attestation_template.reload + end + + it { expect(attestation_template.activated).to be false } + it { expect(flash.notice).to eq("L'attestation a bien été désactivée") } + end + end +end From c0facbf6793d7956d5e3dc99713fe109c0387146 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Wed, 31 May 2017 17:39:15 +0200 Subject: [PATCH 037/136] [Fix #196] AttestionTemplate: add preview --- .../admin/attestation_templates_controller.rb | 15 +++++++ .../attestation_templates/edit.html.haml | 2 + .../attestation_templates/show.pdf.prawn | 38 ++++++++++++++++++ config/routes.rb | 3 ++ lib/prawn/fonts/OpenSans-Regular.ttf | Bin 0 -> 217360 bytes .../attestation_templates_controller_spec.rb | 32 +++++++++++++++ 6 files changed, 90 insertions(+) create mode 100644 app/views/admin/attestation_templates/show.pdf.prawn create mode 100644 lib/prawn/fonts/OpenSans-Regular.ttf diff --git a/app/controllers/admin/attestation_templates_controller.rb b/app/controllers/admin/attestation_templates_controller.rb index ca96f235e..bc3bf0f40 100644 --- a/app/controllers/admin/attestation_templates_controller.rb +++ b/app/controllers/admin/attestation_templates_controller.rb @@ -39,6 +39,21 @@ class Admin::AttestationTemplatesController < AdminController redirect_to edit_admin_procedure_attestation_template_path(@procedure) end + def preview + @title = activated_attestation_params[:title] + @body = activated_attestation_params[:body] + @footer = activated_attestation_params[:footer] + @created_at = DateTime.now + + # In a case of a preview, when the user does not change its images, + # the images are not uploaded and thus should be retrieved from previous + # attestation_template + @logo = activated_attestation_params[:logo] || @procedure.attestation_template&.logo + @signature = activated_attestation_params[:signature] || @procedure.attestation_template&.signature + + render 'admin/attestation_templates/show', formats: [:pdf] + end + private def activated_attestation_params diff --git a/app/views/admin/attestation_templates/edit.html.haml b/app/views/admin/attestation_templates/edit.html.haml index 9b3069919..5c50f5023 100644 --- a/app/views/admin/attestation_templates/edit.html.haml +++ b/app/views/admin/attestation_templates/edit.html.haml @@ -61,6 +61,8 @@ ~ f.text_area :footer, class: 'form-control', rows: 2, placeholder: "Direction interministérielle du numérique et du système d'information et de communication de l'Etat (DINSIC)" + %button.btn.btn-primary{ formaction: admin_procedure_attestation_template_preview_path, formtarget: '_blank' } Prévisualiser + .pull-right - if @attestation_template.activated %button.btn.btn-warning{ formaction: admin_procedure_attestation_template_disactivate_path } désactiver l'attestation diff --git a/app/views/admin/attestation_templates/show.pdf.prawn b/app/views/admin/attestation_templates/show.pdf.prawn new file mode 100644 index 000000000..228bdc375 --- /dev/null +++ b/app/views/admin/attestation_templates/show.pdf.prawn @@ -0,0 +1,38 @@ +require 'prawn/measurement_extensions' + +prawn_document(margin: [50, 100, 20, 100]) do |pdf| + pdf.font_families.update( 'open sans' => { normal: './lib/prawn/fonts/OpenSans-Regular.ttf' }) + pdf.font 'open sans' + + grey = '555555' + black = '333333' + max_logo_size = 40.mm + max_signature_size = 40.mm + + pdf.bounding_box([0, pdf.cursor], width: 400, height: 650) do + if @logo.present? + pdf.image StringIO.new(@logo.read), fit: [max_logo_size , max_logo_size], position: :center + end + + pdf.fill_color grey + pdf.pad_top(40) { pdf.text "le #{l(@created_at, format: '%e %B %Y')}", size: 10, align: :right, character_spacing: -0.5 } + + pdf.fill_color black + pdf.pad_top(40) { pdf.text @title, size: 18, character_spacing: -0.2 } + + pdf.fill_color grey + pdf.pad_top(30) { pdf.text @body, size: 10, character_spacing: -0.2 } + + if @signature.present? + pdf.pad_top(40) do + pdf.image StringIO.new(@signature.read), fit: [max_signature_size , max_signature_size], position: :right + end + end + end + + pdf.repeat(:all) do + pdf.move_cursor_to 20 + pdf.fill_color grey + pdf.text @footer, align: :center, size: 8 + end +end diff --git a/config/routes.rb b/config/routes.rb index 0a2075ca8..41cac5e7d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -148,6 +148,9 @@ Rails.application.routes.draw do post 'attestation_template/disactivate' => 'attestation_templates#disactivate' patch 'attestation_template/disactivate' => 'attestation_templates#disactivate' + + post 'attestation_template/preview' => 'attestation_templates#preview' + patch 'attestation_template/preview' => 'attestation_templates#preview' end namespace :accompagnateurs do diff --git a/lib/prawn/fonts/OpenSans-Regular.ttf b/lib/prawn/fonts/OpenSans-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..db433349b7047f72f40072630c1bc110620bf09e GIT binary patch literal 217360 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 !}#gv~NV!5|5YF|kE)J0y^zWbk4>=%|6F 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 literal 0 HcmV?d00001 diff --git a/spec/controllers/admin/attestation_templates_controller_spec.rb b/spec/controllers/admin/attestation_templates_controller_spec.rb index 7f40c6ecc..3e620e764 100644 --- a/spec/controllers/admin/attestation_templates_controller_spec.rb +++ b/spec/controllers/admin/attestation_templates_controller_spec.rb @@ -7,6 +7,38 @@ describe Admin::AttestationTemplatesController, type: :controller do before do sign_in admin + Timecop.freeze(Time.now) + end + + describe 'POST #preview' do + let(:upload_params) { { title: 't', body: 'b', footer: 'f' } } + + before do + post :preview, + params: { procedure_id: procedure.id, + attestation_template: upload_params } + end + + context 'if an attestation template does not exist on the procedure' do + let(:attestation_template) { nil } + it { expect(subject.status).to eq(200) } + it { expect(assigns).to include(upload_params.stringify_keys) } + end + + context 'if an attestation template exists on the procedure' do + context 'with logos' do + let!(:attestation_template) do + create(:attestation_template, logo: logo, signature: signature) + end + + it { expect(subject.status).to eq(200) } + it { expect(assigns).to include(upload_params.stringify_keys) } + it { expect(assigns[:created_at]).to eq(DateTime.now) } + it { expect(assigns(:logo).read).to eq(logo.read) } + it { expect(assigns(:signature).read).to eq(signature.read) } + after { procedure.attestation_template.destroy } + end + end end describe 'GET #edit' do From b443b5cefd2bd5343d832646f60d9ef38171c033 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Fri, 2 Jun 2017 14:30:26 +0200 Subject: [PATCH 038/136] [Fix #196] Dossier: add Attestation --- app/models/attestation.rb | 5 +++++ app/models/dossier.rb | 1 + app/uploaders/attestation_uploader.rb | 20 +++++++++++++++++++ .../20170601123221_create_attestations.rb | 11 ++++++++++ db/schema.rb | 12 ++++++++++- 5 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 app/models/attestation.rb create mode 100644 app/uploaders/attestation_uploader.rb create mode 100644 db/migrate/20170601123221_create_attestations.rb diff --git a/app/models/attestation.rb b/app/models/attestation.rb new file mode 100644 index 000000000..605ed11a4 --- /dev/null +++ b/app/models/attestation.rb @@ -0,0 +1,5 @@ +class Attestation < ApplicationRecord + belongs_to :dossier + + mount_uploader :pdf, AttestationUploader +end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 6fbdfb1a2..47adb661a 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -23,6 +23,7 @@ class Dossier < ActiveRecord::Base has_one :etablissement, dependent: :destroy has_one :entreprise, dependent: :destroy has_one :individual, dependent: :destroy + has_one :attestation has_many :cerfa, dependent: :destroy has_many :pieces_justificatives, dependent: :destroy diff --git a/app/uploaders/attestation_uploader.rb b/app/uploaders/attestation_uploader.rb new file mode 100644 index 000000000..2697d330a --- /dev/null +++ b/app/uploaders/attestation_uploader.rb @@ -0,0 +1,20 @@ +class AttestationUploader < BaseUploader + def root + File.join(Rails.root, 'public') + end + + # Choose what kind of storage to use for this uploader: + if Features.remote_storage + storage :fog + else + storage :file + end + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: + def store_dir + unless Features.remote_storage + "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" + end + end +end diff --git a/db/migrate/20170601123221_create_attestations.rb b/db/migrate/20170601123221_create_attestations.rb new file mode 100644 index 000000000..52bf54664 --- /dev/null +++ b/db/migrate/20170601123221_create_attestations.rb @@ -0,0 +1,11 @@ +class CreateAttestations < ActiveRecord::Migration[5.0] + def change + create_table :attestations do |t| + t.string :pdf + t.string :title + t.references :dossier, foreign_key: true, null: false + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 10d5778c5..842509ac5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170524140630) do +ActiveRecord::Schema.define(version: 20170601123221) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -84,6 +84,15 @@ ActiveRecord::Schema.define(version: 20170524140630) do t.index ["procedure_id"], name: "index_attestation_templates_on_procedure_id", unique: true, using: :btree end + create_table "attestations", force: :cascade do |t| + t.string "pdf" + t.string "title" + t.integer "dossier_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["dossier_id"], name: "index_attestations_on_dossier_id", using: :btree + end + create_table "avis", force: :cascade do |t| t.string "email" t.text "introduction" @@ -463,6 +472,7 @@ ActiveRecord::Schema.define(version: 20170524140630) do end add_foreign_key "attestation_templates", "procedures" + add_foreign_key "attestations", "dossiers" add_foreign_key "avis", "gestionnaires", column: "claimant_id" add_foreign_key "cerfas", "dossiers" add_foreign_key "closed_mails", "procedures" From 602527a09d493aecd06ec2f32608670f6aae8988 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Thu, 8 Jun 2017 14:04:47 +0200 Subject: [PATCH 039/136] [Fix #196] Attestation: building logic from template --- app/models/attestation_template.rb | 83 +++++++++-- app/models/dossier.rb | 9 ++ spec/models/attestation_template_spec.rb | 168 +++++++++++++++++++++++ spec/models/dossier_spec.rb | 30 ++++ 4 files changed, 282 insertions(+), 8 deletions(-) diff --git a/app/models/attestation_template.rb b/app/models/attestation_template.rb index cd625e39d..4b36d53ef 100644 --- a/app/models/attestation_template.rb +++ b/app/models/attestation_template.rb @@ -20,6 +20,10 @@ class AttestationTemplate < ApplicationRecord identity_tags + dossier_tags + procedure_type_de_champ_public_private_tags end + def attestation_for(dossier) + Attestation.new(title: replace_tags(title, dossier), pdf: build_pdf(dossier)) + end + def dup result = AttestationTemplate.new(title: title, body: body, footer: footer, activated: activated) @@ -58,19 +62,82 @@ class AttestationTemplate < ApplicationRecord end def individual_tags - [{ libelle: 'civilité', description: 'M., Mme' }, - { libelle: 'nom', description: "nom de l'usager" }, - { libelle: 'prénom', description: "prénom de l'usager" }] + [{ libelle: 'civilité', description: 'M., Mme', target: 'gender' }, + { libelle: 'nom', description: "nom de l'usager", target: 'nom' }, + { libelle: 'prénom', description: "prénom de l'usager", target: 'prenom' }] end def entreprise_tags - [{ libelle: 'SIREN', description: '' }, - { libelle: 'numéro de TVA intracommunautaire', description: '' }, - { libelle: 'SIRET du siège social', description: '' }, - { libelle: 'raison sociale', description: '' }] + [{ libelle: 'SIREN', description: '', target: 'siren' }, + { libelle: 'numéro de TVA intracommunautaire', description: '', target: 'numero_tva_intracommunautaire' }, + { libelle: 'SIRET du siège social', description: '', target: 'siret_siege_social' }, + { libelle: 'raison sociale', description: '', target: 'raison_sociale' }] end def etablissement_tags - [{ libelle: 'adresse', description: '' }] + [{ libelle: 'adresse', description: '', target: 'adresse' }] + end + + def build_pdf(dossier) + action_view = ActionView::Base.new(ActionController::Base.view_paths, + logo: logo, + title: replace_tags(title, dossier), + body: replace_tags(body, dossier), + signature: signature, + footer: footer, + created_at: Time.now) + + attestation_view = action_view.render(file: 'admin/attestation_templates/show', + formats: [:pdf]) + + view_to_memory_file(attestation_view) + end + + def view_to_memory_file(view) + pdf = StringIO.new(view) + + def pdf.original_filename + 'attestation' + end + + pdf + end + + def replace_tags(text, dossier) + if text.nil? + return '' + end + + text = replace_type_de_champ_tags(text, procedure.types_de_champ, dossier.champs) + text = replace_type_de_champ_tags(text, procedure.types_de_champ_private, dossier.champs_private) + + tags_and_datas = [ + [dossier_tags, dossier], + [individual_tags, dossier.individual], + [entreprise_tags, dossier.entreprise], + [etablissement_tags, dossier.entreprise&.etablissement]] + + tags_and_datas.inject(text) { |acc, (tags, data)| replace_tags_with_values_from_data(acc, tags, data) } + end + + def replace_type_de_champ_tags(text, types_de_champ, dossier_champs) + types_de_champ.inject(text) do |acc, tag| + value = dossier_champs + .select { |champ| champ.libelle == tag[:libelle] } + .first + .value + + acc.gsub("--#{tag[:libelle]}--", value.to_s) + end + end + + def replace_tags_with_values_from_data(text, tags, data) + if data.present? + tags.inject(text) do |acc, tag| + acc.gsub("--#{tag[:libelle]}--", data.send(tag[:target].to_sym).to_s) + end + else + text + end end end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 47adb661a..5eb69e305 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -170,6 +170,9 @@ class Dossier < ActiveRecord::Base end when 'close' if received? + self.attestation = build_attestation + save + closed! if motivation @@ -306,6 +309,12 @@ class Dossier < ActiveRecord::Base private + def build_attestation + if procedure.attestation_template.present? && procedure.attestation_template.activated? + procedure.attestation_template.attestation_for(self) + end + end + def update_state_dates if initiated? && !self.initiated_at self.initiated_at = DateTime.now diff --git a/spec/models/attestation_template_spec.rb b/spec/models/attestation_template_spec.rb index f5d7c62bb..873d2a112 100644 --- a/spec/models/attestation_template_spec.rb +++ b/spec/models/attestation_template_spec.rb @@ -73,4 +73,172 @@ describe AttestationTemplate, type: :model do it { expect(subject.signature.file.read).to eq(attestation_template.signature.file.read) } end end + + describe 'attestation_for' do + let(:procedure) do + create(:procedure, + types_de_champ: types_de_champ, + types_de_champ_private: types_de_champ_private, + for_individual: for_individual) + end + let(:for_individual) { false } + let(:individual) { nil } + let(:etablissement) { nil } + let(:entreprise) { create(:entreprise, etablissement: etablissement) } + let(:types_de_champ) { [] } + let(:types_de_champ_private) { [] } + let(:dossier) { create(:dossier, procedure: procedure, individual: individual, entreprise: entreprise) } + let(:template_title) { 'title' } + let(:template_body) { 'body' } + let(:attestation_template) do + AttestationTemplate.new(procedure: procedure, + title: template_title, + body: template_body, + logo: @logo, + signature: @signature) + end + + before do + @logo = File.open('spec/fixtures/white.png') + @signature = File.open('spec/fixtures/black.png') + Timecop.freeze(Time.now) + end + + after do + @logo.close + @signature.close + end + + let(:view_args) do + original_new = ActionView::Base.method(:new) + arguments = nil + + allow(ActionView::Base).to receive(:new) do |paths, args| + arguments = args + original_new.call(paths, args) + end + + attestation_template.attestation_for(dossier) + + arguments + end + + let(:attestation) { attestation_template.attestation_for(dossier) } + + it 'provides a pseudo file' do + expect(attestation.pdf.file).to exist + expect(attestation.pdf.filename).to eq('attestation') + end + + context 'when the dossier and the procedure has an individual' do + let(:for_individual) { true } + let(:individual) { Individual.create(nom: 'nom', prenom: 'prenom', gender: 'Mme') } + + context 'and the template title use the individual tags' do + let(:template_title) { '--civilité-- --nom-- --prénom--' } + + it { expect(view_args[:title]).to eq('Mme nom prenom') } + end + end + + context 'when the dossier and the procedure has an entreprise' do + let(:for_individual) { false } + + context 'and the template title use the entreprise tags' do + let(:template_title) do + '--SIREN-- --numéro de TVA intracommunautaire-- --SIRET du siège social-- --raison sociale-- --adresse--' + end + + let(:expected_title) do + "#{entreprise.siren} #{entreprise.numero_tva_intracommunautaire} #{entreprise.siret_siege_social} #{entreprise.raison_sociale} --adresse--" + end + + it { expect(view_args[:title]).to eq(expected_title) } + + context 'and the entreprise has a etablissement with an adresse' do + let(:etablissement) { create(:etablissement, adresse: 'adresse') } + let(:template_title) { '--adresse--' } + + it { expect(view_args[:title]).to eq('adresse') } + end + end + end + + context 'when the procedure has a type de champ named libelleA et libelleB' do + let(:types_de_champ) do + [create(:type_de_champ_public, libelle: 'libelleA'), + create(:type_de_champ_public, libelle: 'libelleB')] + end + + context 'and the template title is nil' do + let(:template_title) { nil } + + it { expect(view_args[:title]).to eq('') } + end + + context 'and it is not used in the template title nor body' do + it { expect(view_args[:title]).to eq('title') } + it { expect(view_args[:body]).to eq('body') } + it { expect(view_args[:created_at]).to eq(Time.now) } + it { expect(view_args[:logo]).to eq(attestation_template.logo) } + it { expect(view_args[:signature]).to eq(attestation_template.signature) } + end + + context 'and the are used in the template title and body' do + let(:template_title) { 'title --libelleA--' } + let(:template_body) { 'body --libelleB--' } + + context 'and their value in the dossier are nil' do + it { expect(view_args[:title]).to eq('title ') } + end + + context 'and their value in the dossier are not nil' do + before :each do + dossier.champs + .select { |champ| champ.libelle == 'libelleA' } + .first + .value = 'libelle1' + + dossier.champs + .select { |champ| champ.libelle == 'libelleB' } + .first + .value = 'libelle2' + end + + it { expect(view_args[:title]).to eq('title libelle1') } + it { expect(view_args[:body]).to eq('body libelle2') } + it { expect(attestation.title).to eq('title libelle1') } + end + end + end + + context 'when the dossier has a motivation' do + let(:dossier) { create(:dossier, motivation: 'motivation') } + + context 'and the title has the motivation tag' do + let(:template_title) { 'title --motivation--' } + + it { expect(view_args[:title]).to eq('title motivation') } + end + end + + context 'when the procedure has a type de champ prive named libelleA' do + let(:types_de_champ_private) { [create(:type_de_champ_private, libelle: 'libelleA')] } + + context 'and the are used in the template title' do + let(:template_title) { 'title --libelleA--' } + + context 'and its value in the dossier are not nil' do + before :each do + dossier.champs_private + .select { |champ| champ.libelle == 'libelleA' } + .first + .value = 'libelle1' + end + + it { expect(view_args[:title]).to eq('title libelle1') } + end + end + end + end end diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index ee164c385..2a6434c5e 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -886,4 +886,34 @@ describe Dossier do expect(ActionMailer::Base.deliveries.size).to eq(0) end end + + describe '.build_attestation' do + let(:attestation_template) { nil } + let(:procedure) { create(:procedure, attestation_template: attestation_template) } + + before :each do + dossier.next_step!('gestionnaire', 'close') + dossier.reload + end + + context 'when the dossier is in received state ' do + let!(:dossier) { create(:dossier, procedure: procedure, state: :received) } + + context 'when the procedure has no attestation' do + it { expect(dossier.attestation).to be_nil } + end + + context 'when the procedure has an unactivated attestation' do + let(:attestation_template) { AttestationTemplate.new(activated: false) } + + it { expect(dossier.attestation).to be_nil } + end + + context 'when the procedure attached has an activated attestation' do + let(:attestation_template) { AttestationTemplate.new(activated: true) } + + it { expect(dossier.attestation).not_to be_nil } + end + end + end end From 671a0575ce35b5cdad04f90606715f48eaaffb69 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Wed, 31 May 2017 16:29:51 +0200 Subject: [PATCH 040/136] [Fix #196] Recapitulatif: user can download its attestation --- app/assets/images/pdf.svg | 1 + app/assets/stylesheets/application.scss | 1 + .../attestation_recapitulatif.scss | 31 +++++++++++++++++++ .../users/recapitulatif_controller.rb | 4 +++ app/views/dossiers/_attestation.html.haml | 14 +++++++++ app/views/dossiers/_dossier_show.html.haml | 2 ++ config/routes.rb | 2 ++ .../attestation_templates_controller_spec.rb | 4 +-- .../users/recapitulatif_controller_spec.rb | 18 +++++++++++ 9 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 app/assets/images/pdf.svg create mode 100644 app/assets/stylesheets/attestation_recapitulatif.scss create mode 100644 app/views/dossiers/_attestation.html.haml diff --git a/app/assets/images/pdf.svg b/app/assets/images/pdf.svg new file mode 100644 index 000000000..88bfb51a6 --- /dev/null +++ b/app/assets/images/pdf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 5368690e7..9375b0288 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -45,6 +45,7 @@ // = require typeahead // = require users // = require attestation_template_edit +// = require attestation_recapitulatif // = require_self // = require bootstrap-datepicker3 diff --git a/app/assets/stylesheets/attestation_recapitulatif.scss b/app/assets/stylesheets/attestation_recapitulatif.scss new file mode 100644 index 000000000..7f3f80008 --- /dev/null +++ b/app/assets/stylesheets/attestation_recapitulatif.scss @@ -0,0 +1,31 @@ +#attestation-recapitulatif { + margin-top: 40px; + + .details { + padding: 15px; + display: flex; + align-items: center; + justify-content: space-between; + background-color: #FFFFFF; + + .left { + position: relative; + padding-left: 30px; + + img { + position: absolute; + left: 0px; + top: 15px; + } + + .title { + font-weight: bold; + margin: 0; + } + + .delivery { + color: #999999; + } + } + } +} diff --git a/app/controllers/users/recapitulatif_controller.rb b/app/controllers/users/recapitulatif_controller.rb index 88dd4d2f8..9d470d500 100644 --- a/app/controllers/users/recapitulatif_controller.rb +++ b/app/controllers/users/recapitulatif_controller.rb @@ -7,6 +7,10 @@ class Users::RecapitulatifController < UsersController create_dossier_facade end + def attestation + send_data(current_user_dossier.attestation.pdf.read, filename: 'attestation.pdf', type: 'application/pdf') + end + def initiate create_dossier_facade diff --git a/app/views/dossiers/_attestation.html.haml b/app/views/dossiers/_attestation.html.haml new file mode 100644 index 000000000..b6c4dc3ce --- /dev/null +++ b/app/views/dossiers/_attestation.html.haml @@ -0,0 +1,14 @@ +- if dossier.attestation.present? + #attestation-recapitulatif.default-data-block + .row.show-block.clearfix + .header + .title + .carret-right + .carret-down + ATTESTATION + .details + .left + = image_tag('pdf.svg', width: '20px') + %p.title= dossier.attestation.title + %p.delivery Délivrée le #{l(dossier.attestation.created_at, format: '%d %B %Y')} + %a.btn.btn-primary{ href: users_dossier_recapitulatif_attestation_path(dossier), target: '_blank' } Télécharger diff --git a/app/views/dossiers/_dossier_show.html.haml b/app/views/dossiers/_dossier_show.html.haml index 60d426479..d5d0ad5f4 100644 --- a/app/views/dossiers/_dossier_show.html.haml +++ b/app/views/dossiers/_dossier_show.html.haml @@ -1,5 +1,7 @@ = render partial: 'dossiers/edit_avis', locals: { dossier_facade: @facade } += render partial: 'dossiers/attestation', locals: { dossier: @facade.dossier } + = render partial: 'dossiers/messagerie', locals: { dossier_facade: @facade } = render partial: 'dossiers/motivation', locals: { dossier_facade: @facade } diff --git a/config/routes.rb b/config/routes.rb index 41cac5e7d..311743cff 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -82,6 +82,8 @@ Rails.application.routes.draw do get '/recapitulatif' => 'recapitulatif#show' post '/recapitulatif/initiate' => 'recapitulatif#initiate' + get '/recapitulatif/attestation' => 'recapitulatif#attestation' + post '/commentaire' => 'commentaires#create' resources :commentaires, only: [:index] diff --git a/spec/controllers/admin/attestation_templates_controller_spec.rb b/spec/controllers/admin/attestation_templates_controller_spec.rb index 3e620e764..cd318832a 100644 --- a/spec/controllers/admin/attestation_templates_controller_spec.rb +++ b/spec/controllers/admin/attestation_templates_controller_spec.rb @@ -15,8 +15,8 @@ describe Admin::AttestationTemplatesController, type: :controller do before do post :preview, - params: { procedure_id: procedure.id, - attestation_template: upload_params } + params: { procedure_id: procedure.id, + attestation_template: upload_params } end context 'if an attestation template does not exist on the procedure' do diff --git a/spec/controllers/users/recapitulatif_controller_spec.rb b/spec/controllers/users/recapitulatif_controller_spec.rb index 120e2e344..326699f3e 100644 --- a/spec/controllers/users/recapitulatif_controller_spec.rb +++ b/spec/controllers/users/recapitulatif_controller_spec.rb @@ -51,4 +51,22 @@ describe Users::RecapitulatifController, type: :controller do end end end + + describe 'Get #attestation' do + context 'when a dossier has an attestation' do + let(:fake_pdf) { double(read: 'pdf content') } + let!(:dossier) { create(:dossier, attestation: Attestation.new) } + + it 'returns the attestation pdf' do + allow_any_instance_of(Attestation).to receive(:pdf).and_return(fake_pdf) + + expect(controller).to receive(:send_data) + .with('pdf content', filename: 'attestation.pdf', type: 'application/pdf') do + controller.render nothing: true + end + + get :attestation, params: { dossier_id: dossier.id } + end + end + end end From b664709c3d54523c6f67f3dc542973efc19514bb Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Fri, 9 Jun 2017 15:26:52 +0200 Subject: [PATCH 041/136] [Fix #196] Attestation: allow vizualisation after the procedure is published --- .../admin/attestation_templates_controller.rb | 10 ++++++++++ ...panel_admin_procedurescontroller_navbar.html.haml | 8 +++++--- config/routes.rb | 2 +- .../admin/attestation_templates_controller_spec.rb | 12 ++++++++++++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/app/controllers/admin/attestation_templates_controller.rb b/app/controllers/admin/attestation_templates_controller.rb index bc3bf0f40..0a6ea60a3 100644 --- a/app/controllers/admin/attestation_templates_controller.rb +++ b/app/controllers/admin/attestation_templates_controller.rb @@ -1,6 +1,16 @@ class Admin::AttestationTemplatesController < AdminController before_action :retrieve_procedure + def show + attestation_template = @procedure.attestation_template + @logo = attestation_template.logo + @title = attestation_template.title + @body = attestation_template.body + @signature = attestation_template.signature + @footer = attestation_template.footer + @created_at = DateTime.now + end + def edit @attestation_template = @procedure.attestation_template || AttestationTemplate.new(procedure: @procedure) end diff --git a/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml b/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml index 60ee10888..94121e4cb 100644 --- a/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml +++ b/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml @@ -45,10 +45,12 @@ .procedure-list-element{ class: ('active' if active == 'Prévisualisation') } Prévisualisation - - unless @procedure.locked? + - if @procedure.locked? + %a#onglet-attestation{ href: url_for(admin_procedure_attestation_template_path(@procedure, format: :pdf)), target: '_blank' } + .procedure-list-element{ class: ('active' if active == 'Attestation') } Attestation + - else %a#onglet-attestation{ href: url_for(edit_admin_procedure_attestation_template_path(@procedure)) } - .procedure-list-element{ class: ('active' if active == 'Attestation') } - Attestation + .procedure-list-element{ class: ('active' if active == 'Attestation') } Attestation .split-hr-left diff --git a/config/routes.rb b/config/routes.rb index 311743cff..699ba278d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -146,7 +146,7 @@ Rails.application.routes.draw do resource :previsualisation, only: [:show] - resource :attestation_template, only: [:edit, :update, :create] + resource :attestation_template, only: [:show, :edit, :update, :create] post 'attestation_template/disactivate' => 'attestation_templates#disactivate' patch 'attestation_template/disactivate' => 'attestation_templates#disactivate' diff --git a/spec/controllers/admin/attestation_templates_controller_spec.rb b/spec/controllers/admin/attestation_templates_controller_spec.rb index cd318832a..7e62dcf43 100644 --- a/spec/controllers/admin/attestation_templates_controller_spec.rb +++ b/spec/controllers/admin/attestation_templates_controller_spec.rb @@ -10,6 +10,18 @@ describe Admin::AttestationTemplatesController, type: :controller do Timecop.freeze(Time.now) end + describe 'GET #show' do + before { get :show, params: { procedure_id: procedure.id, format: :pdf } } + + it { expect(subject.status).to eq(200) } + it { expect(assigns[:title]).to eq(attestation_template.title) } + it { expect(assigns[:logo].read).to eq(attestation_template.logo.read) } + it { expect(assigns[:body]).to eq(attestation_template.body) } + it { expect(assigns[:signature].read).to eq(attestation_template.signature.read) } + it { expect(assigns[:footer]).to eq(attestation_template.footer) } + it { expect(assigns[:created_at]).to eq(DateTime.now) } + end + describe 'POST #preview' do let(:upload_params) { { title: 't', body: 'b', footer: 'f' } } From e60ce35ae8f3747b3fb8770ee27ba90add9b99e0 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Fri, 9 Jun 2017 22:29:48 +0200 Subject: [PATCH 042/136] [Fix #196] Attestation: join the attestation to the closed mail Add a upper limit to the attachment size as it could be a problem with Mailjet and receiver (https://www.mailjet.com/support/what-is-the-size-limit-for-attachments-files-sent-via-mailjet,289.htm) If the attestation cannot be sent, it is logged in sentry --- Gemfile | 7 +-- .../backoffice/dossiers_controller.rb | 19 ++++++- app/mailers/notification_mailer.rb | 6 ++- app/models/attestation.rb | 6 +++ .../backoffice/dossiers_controller_spec.rb | 53 +++++++++++++++---- spec/mailers/notification_mailer_spec.rb | 18 ++++++- spec/models/attestation_spec.rb | 23 ++++++++ 7 files changed, 114 insertions(+), 18 deletions(-) create mode 100644 spec/models/attestation_spec.rb diff --git a/Gemfile b/Gemfile index c6e525175..c7b560dd2 100644 --- a/Gemfile +++ b/Gemfile @@ -106,6 +106,8 @@ gem 'select2-rails' gem 'prawn', '~> 2.0.1' gem 'prawn_rails', '~> 0.0.11' +gem 'sentry-raven' + group :test do gem 'capybara' gem 'launchy' @@ -148,8 +150,3 @@ group :development, :test do # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'dotenv-rails' end - -group :production, :staging do - gem 'sentry-raven' -end - diff --git a/app/controllers/backoffice/dossiers_controller.rb b/app/controllers/backoffice/dossiers_controller.rb index a5477430f..9ab9c4d08 100644 --- a/app/controllers/backoffice/dossiers_controller.rb +++ b/app/controllers/backoffice/dossiers_controller.rb @@ -1,4 +1,6 @@ class Backoffice::DossiersController < Backoffice::DossiersListController + include ActionView::Helpers::NumberHelper + respond_to :html, :xlsx, :ods, :csv prepend_before_action :store_current_location, only: :show @@ -112,6 +114,8 @@ class Backoffice::DossiersController < Backoffice::DossiersListController dossier = @facade.dossier + attestation_pdf = nil + case params[:process_action] when "refuse" next_step = "refuse" @@ -125,12 +129,15 @@ class Backoffice::DossiersController < Backoffice::DossiersListController next_step = "close" notice = "Dossier traité avec succès." template = dossier.procedure.closed_mail_template + if check_attestation_emailable(dossier) + attestation_pdf = dossier.attestation.pdf.read + end end dossier.next_step! 'gestionnaire', next_step, motivation flash.notice = notice - NotificationMailer.send_notification(dossier, template).deliver_now! + NotificationMailer.send_notification(dossier, template, attestation_pdf).deliver_now! redirect_to backoffice_dossier_path(id: dossier.id) end @@ -185,6 +192,16 @@ class Backoffice::DossiersController < Backoffice::DossiersListController private + def check_attestation_emailable(dossier) + if dossier&.attestation&.emailable? == false + human_size = number_to_human_size(dossier.attestation.pdf.size) + msg = "the attestation of the dossier #{dossier.id} cannot be mailed because it is too heavy: #{human_size}" + capture_message(msg, level: 'error') + end + + dossier&.attestation&.emailable? + end + def store_current_location if !gestionnaire_signed_in? store_location_for(:gestionnaire, request.url) diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb index 6c7d4b91c..6c75975df 100644 --- a/app/mailers/notification_mailer.rb +++ b/app/mailers/notification_mailer.rb @@ -3,12 +3,16 @@ class NotificationMailer < ApplicationMailer after_action :create_commentaire_for_notification, only: :send_notification - def send_notification(dossier, mail_template) + def send_notification(dossier, mail_template, attestation = nil) vars_mailer(dossier) @object = mail_template.object_for_dossier dossier @body = mail_template.body_for_dossier dossier + if attestation.present? + attachments['attestation.pdf'] = attestation + end + mail(subject: @object) { |format| format.html { @body } } end diff --git a/app/models/attestation.rb b/app/models/attestation.rb index 605ed11a4..220e2c364 100644 --- a/app/models/attestation.rb +++ b/app/models/attestation.rb @@ -2,4 +2,10 @@ class Attestation < ApplicationRecord belongs_to :dossier mount_uploader :pdf, AttestationUploader + + MAX_SIZE_EMAILABLE = 2.megabytes + + def emailable? + pdf.size <= MAX_SIZE_EMAILABLE + end end diff --git a/spec/controllers/backoffice/dossiers_controller_spec.rb b/spec/controllers/backoffice/dossiers_controller_spec.rb index 5d11f4b2d..e019314ba 100644 --- a/spec/controllers/backoffice/dossiers_controller_spec.rb +++ b/spec/controllers/backoffice/dossiers_controller_spec.rb @@ -268,7 +268,7 @@ describe Backoffice::DossiersController, type: :controller do it 'Notification email is sent' do expect(NotificationMailer).to receive(:send_notification) - .with(dossier, kind_of(Mails::RefusedMail)).and_return(NotificationMailer) + .with(dossier, kind_of(Mails::RefusedMail), nil).and_return(NotificationMailer) expect(NotificationMailer).to receive(:deliver_now!) subject @@ -294,7 +294,7 @@ describe Backoffice::DossiersController, type: :controller do it 'Notification email is sent' do expect(NotificationMailer).to receive(:send_notification) - .with(dossier, kind_of(Mails::WithoutContinuationMail)).and_return(NotificationMailer) + .with(dossier, kind_of(Mails::WithoutContinuationMail), nil).and_return(NotificationMailer) expect(NotificationMailer).to receive(:deliver_now!) subject @@ -304,9 +304,17 @@ describe Backoffice::DossiersController, type: :controller do end context "with close" do + let(:expected_attestation) { nil } + before do dossier.received! sign_in gestionnaire + + expect(NotificationMailer).to receive(:send_notification) + .with(dossier, kind_of(Mails::ClosedMail), expected_attestation) + .and_return(NotificationMailer) + + expect(NotificationMailer).to receive(:deliver_now!) end subject { post :process_dossier, params: { process_action: "close", dossier_id: dossier_id} } @@ -318,15 +326,42 @@ describe Backoffice::DossiersController, type: :controller do expect(dossier.state).to eq('closed') end - it 'Notification email is sent' do - expect(NotificationMailer).to receive(:send_notification) - .with(dossier, kind_of(Mails::ClosedMail)).and_return(NotificationMailer) - expect(NotificationMailer).to receive(:deliver_now!) - - subject + context 'when the dossier does not have any attestation' do + it 'Notification email is sent' do + subject + end end - it { is_expected.to redirect_to backoffice_dossier_path(id: dossier.id) } + context 'when the dossier has an attestation' do + let(:emailable) { false } + + before do + fake_attestation = double(pdf: double(read: 'pdf', size: 2.megabytes), emailable?: emailable) + allow_any_instance_of(Dossier).to receive(:attestation).and_return(fake_attestation) + end + + context 'emailable' do + let(:emailable) { true } + let(:expected_attestation) { 'pdf' } + + it 'Notification email is sent with the attestation' do + subject + end + end + + context 'when the dossier has an attestation not emailable' do + let(:emailable) { false } + let(:expected_attestation) { nil } + + it 'Notification email is sent without the attestation' do + expect(controller).to receive(:capture_message) + + subject + end + end + + it { is_expected.to redirect_to backoffice_dossier_path(id: dossier.id) } + end end end diff --git a/spec/mailers/notification_mailer_spec.rb b/spec/mailers/notification_mailer_spec.rb index e9a66e4a7..9fcfba7bf 100644 --- a/spec/mailers/notification_mailer_spec.rb +++ b/spec/mailers/notification_mailer_spec.rb @@ -5,11 +5,25 @@ RSpec.describe NotificationMailer, type: :mailer do let(:user) { create(:user) } let(:dossier) { create(:dossier, user: user) } let(:email) { instance_double('email', object_for_dossier: 'object', body_for_dossier: 'body') } - let (:notifications_count_before) { Notification.count } - subject { described_class.send_notification(dossier, email) } + let(:attestation) { nil } + let(:notifications_count_before) { Notification.count } + + subject { described_class.send_notification(dossier, email, attestation) } it { expect(subject.subject).to eq(email.object_for_dossier) } it { expect(subject.body).to eq(email.body_for_dossier) } + it { expect(subject.attachments['attestation.pdf']).to eq(nil) } + + context 'when an attestation is provided' do + let(:attestation) { 'attestation' } + + it do + expect(subject.attachments['attestation.pdf'].content_type) + .to eq('application/pdf; filename=attestation.pdf') + + expect(subject.attachments['attestation.pdf'].body).to eq('attestation') + end + end it "creates a commentaire, which is not notified" do described_class.send_notification(dossier, email).deliver_now diff --git a/spec/models/attestation_spec.rb b/spec/models/attestation_spec.rb new file mode 100644 index 000000000..5bf9e89f5 --- /dev/null +++ b/spec/models/attestation_spec.rb @@ -0,0 +1,23 @@ +RSpec.describe Attestation, type: :model do + describe 'emailable' do + let(:attestation) do + attestation = Attestation.new + expect(attestation).to receive(:pdf).and_return(double(size: size)) + attestation + end + + subject { attestation.emailable? } + + context 'when the pdf size is acceptable' do + let(:size) { Attestation::MAX_SIZE_EMAILABLE } + + it { is_expected.to be true } + end + + context 'when the pdf size is unacceptable' do + let(:size) { Attestation::MAX_SIZE_EMAILABLE + 1 } + + it { is_expected.to be false } + end + end +end From 5c70d38a2376db10b504ca3a62465585a97a17ac Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Fri, 16 Jun 2017 14:10:08 +0200 Subject: [PATCH 043/136] Attestation: fix missing_attachment --- app/controllers/backoffice/dossiers_controller.rb | 11 ++++++----- .../backoffice/dossiers_controller_spec.rb | 7 +++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/controllers/backoffice/dossiers_controller.rb b/app/controllers/backoffice/dossiers_controller.rb index 9ab9c4d08..0e3fea2b4 100644 --- a/app/controllers/backoffice/dossiers_controller.rb +++ b/app/controllers/backoffice/dossiers_controller.rb @@ -114,8 +114,6 @@ class Backoffice::DossiersController < Backoffice::DossiersListController dossier = @facade.dossier - attestation_pdf = nil - case params[:process_action] when "refuse" next_step = "refuse" @@ -129,12 +127,15 @@ class Backoffice::DossiersController < Backoffice::DossiersListController next_step = "close" notice = "Dossier traité avec succès." template = dossier.procedure.closed_mail_template - if check_attestation_emailable(dossier) - attestation_pdf = dossier.attestation.pdf.read - end end dossier.next_step! 'gestionnaire', next_step, motivation + + attestation_pdf = nil + if check_attestation_emailable(dossier) + attestation_pdf = dossier.attestation.pdf.read + end + flash.notice = notice NotificationMailer.send_notification(dossier, template, attestation_pdf).deliver_now! diff --git a/spec/controllers/backoffice/dossiers_controller_spec.rb b/spec/controllers/backoffice/dossiers_controller_spec.rb index e019314ba..60486279d 100644 --- a/spec/controllers/backoffice/dossiers_controller_spec.rb +++ b/spec/controllers/backoffice/dossiers_controller_spec.rb @@ -336,8 +336,11 @@ describe Backoffice::DossiersController, type: :controller do let(:emailable) { false } before do - fake_attestation = double(pdf: double(read: 'pdf', size: 2.megabytes), emailable?: emailable) - allow_any_instance_of(Dossier).to receive(:attestation).and_return(fake_attestation) + attestation = Attestation.new + allow(attestation).to receive(:pdf).and_return(double(read: 'pdf', size: 2.megabytes)) + allow(attestation).to receive(:emailable?).and_return(emailable) + + allow_any_instance_of(Dossier).to receive(:build_attestation).and_return(attestation) end context 'emailable' do From 523df76ecb5ab641f607966930b21cfeb053fc98 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Fri, 16 Jun 2017 13:18:38 +0200 Subject: [PATCH 044/136] Attestation: fix attestation_template_admin_link --- ...left_panel_admin_procedurescontroller_navbar.html.haml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml b/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml index 94121e4cb..37e2fe106 100644 --- a/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml +++ b/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml @@ -45,12 +45,12 @@ .procedure-list-element{ class: ('active' if active == 'Prévisualisation') } Prévisualisation - - if @procedure.locked? - %a#onglet-attestation{ href: url_for(admin_procedure_attestation_template_path(@procedure, format: :pdf)), target: '_blank' } - .procedure-list-element{ class: ('active' if active == 'Attestation') } Attestation - - else + - if !@procedure.locked? %a#onglet-attestation{ href: url_for(edit_admin_procedure_attestation_template_path(@procedure)) } .procedure-list-element{ class: ('active' if active == 'Attestation') } Attestation + - elsif @procedure.attestation_template.present? + %a#onglet-attestation{ href: url_for(admin_procedure_attestation_template_path(@procedure, format: :pdf)), target: '_blank' } + .procedure-list-element Attestation .split-hr-left From dd441ff0c77cdf488832ac0e40d5ab3e30b3b391 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Mon, 19 Jun 2017 13:47:00 +0200 Subject: [PATCH 045/136] Attestation: fix mail attachment when the files are remotely stored --- app/controllers/backoffice/dossiers_controller.rb | 4 ++++ spec/controllers/backoffice/dossiers_controller_spec.rb | 1 + 2 files changed, 5 insertions(+) diff --git a/app/controllers/backoffice/dossiers_controller.rb b/app/controllers/backoffice/dossiers_controller.rb index 0e3fea2b4..0c153122d 100644 --- a/app/controllers/backoffice/dossiers_controller.rb +++ b/app/controllers/backoffice/dossiers_controller.rb @@ -131,6 +131,10 @@ class Backoffice::DossiersController < Backoffice::DossiersListController dossier.next_step! 'gestionnaire', next_step, motivation + # needed to force Carrierwave to provide dossier.attestation.pdf.read + # when the Feature.remote_storage is true, otherwise pdf.read is a closed stream. + dossier.reload + attestation_pdf = nil if check_attestation_emailable(dossier) attestation_pdf = dossier.attestation.pdf.read diff --git a/spec/controllers/backoffice/dossiers_controller_spec.rb b/spec/controllers/backoffice/dossiers_controller_spec.rb index 60486279d..a619ff020 100644 --- a/spec/controllers/backoffice/dossiers_controller_spec.rb +++ b/spec/controllers/backoffice/dossiers_controller_spec.rb @@ -340,6 +340,7 @@ describe Backoffice::DossiersController, type: :controller do allow(attestation).to receive(:pdf).and_return(double(read: 'pdf', size: 2.megabytes)) allow(attestation).to receive(:emailable?).and_return(emailable) + expect_any_instance_of(Dossier).to receive(:reload) allow_any_instance_of(Dossier).to receive(:build_attestation).and_return(attestation) end From ce2def9c40e60512569809e6d3c23c1ba457e3b3 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Thu, 15 Jun 2017 17:36:30 +0200 Subject: [PATCH 046/136] Expose the motivation on the API --- app/serializers/dossier_serializer.rb | 1 + doc/apipie_examples.json | 1 + spec/controllers/api/v1/dossiers_controller_spec.rb | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/serializers/dossier_serializer.rb b/app/serializers/dossier_serializer.rb index c731d452b..70693205f 100644 --- a/app/serializers/dossier_serializer.rb +++ b/app/serializers/dossier_serializer.rb @@ -9,6 +9,7 @@ class DossierSerializer < ActiveModel::Serializer :initiated_at, :received_at, :processed_at, + :motivation, :accompagnateurs, :invites diff --git a/doc/apipie_examples.json b/doc/apipie_examples.json index 7010bc322..f67e5140d 100644 --- a/doc/apipie_examples.json +++ b/doc/apipie_examples.json @@ -50,6 +50,7 @@ "initiated_at": "2017-04-11T12:00:12.000Z", "received_at": null, "processed_at": null, + "motivation": null, "accompagnateurs": [ "gestionnaire@apientreprise.fr" ], diff --git a/spec/controllers/api/v1/dossiers_controller_spec.rb b/spec/controllers/api/v1/dossiers_controller_spec.rb index 809711108..efb1d8cf2 100644 --- a/spec/controllers/api/v1/dossiers_controller_spec.rb +++ b/spec/controllers/api/v1/dossiers_controller_spec.rb @@ -111,10 +111,10 @@ describe API::V1::DossiersController do context 'when dossier exists and belongs to procedure' do let(:procedure_id) { procedure.id } let(:date_creation) { Time.utc(2008, 9, 1, 10, 5, 0) } - let!(:dossier) { Timecop.freeze(date_creation) { create(:dossier, :with_entreprise, procedure: procedure) } } + let!(:dossier) { Timecop.freeze(date_creation) { create(:dossier, :with_entreprise, procedure: procedure, motivation: "Motivation") } } let(:dossier_id) { dossier.id } let(:body) { JSON.parse(retour.body, symbolize_names: true) } - let(:field_list) { [:id, :created_at, :updated_at, :archived, :mandataire_social, :entreprise, :etablissement, :cerfa, :types_de_piece_justificative, :pieces_justificatives, :champs, :champs_private, :commentaires, :state, :simplified_state, :initiated_at, :processed_at, :received_at, :accompagnateurs, :invites] } + let(:field_list) { [:id, :created_at, :updated_at, :archived, :mandataire_social, :entreprise, :etablissement, :cerfa, :types_de_piece_justificative, :pieces_justificatives, :champs, :champs_private, :commentaires, :state, :simplified_state, :initiated_at, :processed_at, :received_at, :motivation, :accompagnateurs, :invites] } subject { body[:dossier] } it 'return REST code 200', :show_in_doc do From 14823560a1bf8345a5a1d5dd221284e4a4131f6b Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 19 Jun 2017 10:20:46 +0200 Subject: [PATCH 047/136] Expose the motivation on exported files --- app/serializers/dossier_table_export_serializer.rb | 3 ++- spec/models/dossier_spec.rb | 14 ++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/serializers/dossier_table_export_serializer.rb b/app/serializers/dossier_table_export_serializer.rb index 84e569787..877b9b6d6 100644 --- a/app/serializers/dossier_table_export_serializer.rb +++ b/app/serializers/dossier_table_export_serializer.rb @@ -7,7 +7,8 @@ class DossierTableExportSerializer < ActiveModel::Serializer :state, :initiated_at, :received_at, - :processed_at + :processed_at, + :motivation attribute :emails_accompagnateurs diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index 2a6434c5e..958afca70 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -492,7 +492,7 @@ describe Dossier do let(:date1) { 1.day.ago } let(:date2) { 1.hour.ago } let(:date3) { 1.minute.ago } - let(:dossier) { create(:dossier, :with_entreprise, user: user, procedure: procedure, follows: [follow], initiated_at: date1, received_at: date2, processed_at: date3) } + let(:dossier) { create(:dossier, :with_entreprise, user: user, procedure: procedure, follows: [follow], initiated_at: date1, received_at: date2, processed_at: date3, motivation: "Motivation") } describe '#export_headers' do subject { dossier.export_headers } @@ -518,10 +518,11 @@ describe Dossier do it { expect(subject[7]).to eq(date2) } it { expect(subject[8]).to eq(date3) } it { expect(subject[9]).to be_a_kind_of(String) } - it { expect(subject[10]).to be_nil } + it { expect(subject[10]).to be_a_kind_of(String) } it { expect(subject[11]).to be_nil } it { expect(subject[12]).to be_nil } it { expect(subject[13]).to be_nil } + it { expect(subject[14]).to be_nil } it { expect(subject.count).to eq(DossierTableExportSerializer.new(dossier).attributes.count + dossier.procedure.types_de_champ.count + dossier.export_entreprise_data.count) } context 'dossier for individual' do @@ -529,10 +530,10 @@ describe Dossier do subject { dossier_with_individual.data_with_champs } - it { expect(subject[10]).to eq(dossier_with_individual.individual.gender) } - it { expect(subject[11]).to eq(dossier_with_individual.individual.prenom) } - it { expect(subject[12]).to eq(dossier_with_individual.individual.nom) } - it { expect(subject[13]).to eq(dossier_with_individual.individual.birthdate) } + it { expect(subject[11]).to eq(dossier_with_individual.individual.gender) } + it { expect(subject[12]).to eq(dossier_with_individual.individual.prenom) } + it { expect(subject[13]).to eq(dossier_with_individual.individual.nom) } + it { expect(subject[14]).to eq(dossier_with_individual.individual.birthdate) } end end @@ -548,6 +549,7 @@ describe Dossier do dossier.initiated_at, dossier.received_at, dossier.processed_at, + "Motivation", gestionnaire.email, nil, nil, From 19696da736eb77d42eb3af11305cb2190a679c6b Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 19 Jun 2017 17:29:20 +0200 Subject: [PATCH 048/136] Add StatsController#percentage --- app/controllers/stats_controller.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb index 5cacd22d1..8f9139ec9 100644 --- a/app/controllers/stats_controller.rb +++ b/app/controllers/stats_controller.rb @@ -70,6 +70,10 @@ class StatsController < ApplicationController (collection.sum.to_f / collection.size).round(2) end + def percentage(numerator, denominator) + ((numerator.to_f / denominator) * 100).round(2) + end + def dossier_instruction_mean_time(dossiers) # In the 12 last months, we compute for each month # the average time it took to instruct a dossier @@ -166,7 +170,7 @@ class StatsController < ApplicationController result = 0 else weekly_dossier_with_avis_count = weekly_dossiers.select { |dossier| dossier.avis.present? }.count - result = ((weekly_dossier_with_avis_count.to_f / weekly_dossiers_count) * 100).round(2) + result = percentage(weekly_dossier_with_avis_count, weekly_dossiers_count) end [min_date.to_i, result] @@ -199,7 +203,7 @@ class StatsController < ApplicationController [min_date.to_i, 0] else answered_weekly_avis_count = weekly_avis.with_answer.count - result = ((answered_weekly_avis_count.to_f / weekly_avis_count) * 100).round(2) + result = percentage(answered_weekly_avis_count, weekly_avis_count) [min_date.to_i, result] end From d0b927857af7be07bd968a6505b0f88ebc1d172e Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Thu, 15 Jun 2017 14:42:48 +0200 Subject: [PATCH 049/136] Add stats for encart motivation --- app/controllers/stats_controller.rb | 51 +++++++++++++++++++++++ app/views/stats/index.html.haml | 13 ++++++ spec/controllers/stats_controller_spec.rb | 29 +++++++++++++ 3 files changed, 93 insertions(+) diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb index 8f9139ec9..e41dc8c74 100644 --- a/app/controllers/stats_controller.rb +++ b/app/controllers/stats_controller.rb @@ -29,6 +29,9 @@ class StatsController < ApplicationController @avis_usage = avis_usage @avis_average_answer_time = avis_average_answer_time @avis_answer_percentages = avis_answer_percentages + + @motivation_usage_dossier = motivation_usage_dossier + @motivation_usage_procedure = motivation_usage_procedure end private @@ -209,4 +212,52 @@ class StatsController < ApplicationController end end end + + def motivation_usage_dossier + [3.week.ago, 2.week.ago, 1.week.ago].map do |date| + min_date = date.beginning_of_week + max_date = date.end_of_week + + weekly_termine_dossiers = Dossier.where(processed_at: min_date..max_date) + weekly_termine_dossiers_count = weekly_termine_dossiers.count + weekly_termine_dossiers_with_motivation_count = weekly_termine_dossiers.where.not(motivation: nil).count + + if weekly_termine_dossiers_count == 0 + result = 0 + else + result = percentage(weekly_termine_dossiers_with_motivation_count, weekly_termine_dossiers_count) + end + + [l(max_date, format: '%d/%m/%Y'), result] + end + end + + def motivation_usage_procedure + [3.week.ago, 2.week.ago, 1.week.ago].map do |date| + min_date = date.beginning_of_week + max_date = date.end_of_week + + procedures_with_dossier_processed_this_week = Procedure + .joins(:dossiers) + .where(dossiers: { processed_at: min_date..max_date }) + + procedures_with_dossier_processed_this_week_count = procedures_with_dossier_processed_this_week + .uniq + .count + + procedures_with_dossier_processed_this_week_and_with_motivation_count = procedures_with_dossier_processed_this_week + .where + .not(dossiers: { motivation: nil }) + .uniq + .count + + if procedures_with_dossier_processed_this_week_count == 0 + result = 0 + else + result = percentage(procedures_with_dossier_processed_this_week_and_with_motivation_count, procedures_with_dossier_processed_this_week_count) + end + + [l(max_date, format: '%d/%m/%Y'), result] + end + end end diff --git a/app/views/stats/index.html.haml b/app/views/stats/index.html.haml index a250973f3..d09269ecc 100644 --- a/app/views/stats/index.html.haml +++ b/app/views/stats/index.html.haml @@ -88,3 +88,16 @@ = line_chart @avis_answer_percentages, ytitle: 'avis avec réponse / total avis', xtitle: 'semaines' .clearfix + + %h2.new-h2 Encart motivation + + .stat-cards + .stat-card.stat-card-half.pull-left + %span.stat-card-title Taux d'utilisation des motivations (par dossier) + = column_chart @motivation_usage_dossier, ytitle: 'dossiers avec motivation / total dossiers', xtitle: 'semaines' + + .stat-card.stat-card-half.pull-left + %span.stat-card-title Taux d'utilisation des motivations (par procédure) + = column_chart @motivation_usage_procedure, ytitle: 'procedures avec motivation / total procedures', xtitle: 'semaines' + + .clearfix diff --git a/spec/controllers/stats_controller_spec.rb b/spec/controllers/stats_controller_spec.rb index 238af0f66..1c28f0d01 100644 --- a/spec/controllers/stats_controller_spec.rb +++ b/spec/controllers/stats_controller_spec.rb @@ -267,4 +267,33 @@ describe StatsController, type: :controller do it { is_expected.to match [[3.week.ago.to_i, 0], [2.week.ago.to_i, 0], [1.week.ago.to_i, 66.67]] } end + + describe '#motivation_usage_dossier' do + let!(:dossier) { create(:dossier, processed_at: 1.week.ago, motivation: "Motivation") } + let!(:dossier2) { create(:dossier, processed_at: 1.week.ago) } + let!(:dossier3) { create(:dossier, processed_at: 1.week.ago) } + + before do + Timecop.freeze(Time.now) + end + + subject { StatsController.new.send(:motivation_usage_dossier) } + + it { expect(subject).to match([[I18n.l(3.week.ago.end_of_week, format: '%d/%m/%Y'), 0], [I18n.l(2.week.ago.end_of_week, format: '%d/%m/%Y'), 0], [I18n.l(1.week.ago.end_of_week, format: '%d/%m/%Y'), 33.33]]) } + end + + describe '#motivation_usage_procedure' do + let!(:dossier) { create(:dossier, processed_at: 1.week.ago, motivation: "Motivation" ) } + let!(:dossier1) { create(:dossier, processed_at: 1.week.ago, motivation: "Motivation", procedure: dossier.procedure) } + let!(:dossier2) { create(:dossier, processed_at: 1.week.ago) } + let!(:dossier3) { create(:dossier, processed_at: 1.week.ago) } + + before do + Timecop.freeze(Time.now) + end + + subject { StatsController.new.send(:motivation_usage_procedure) } + + it { expect(subject).to match([[I18n.l(3.week.ago.end_of_week, format: '%d/%m/%Y'), 0], [I18n.l(2.week.ago.end_of_week, format: '%d/%m/%Y'), 0], [I18n.l(1.week.ago.end_of_week, format: '%d/%m/%Y'), 33.33]]) } + end end From 0bed3ae956e15b1ac0e8183429d1fb16089940b2 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 13 Jun 2017 12:33:03 +0200 Subject: [PATCH 050/136] Use new layout for login --- app/controllers/users/sessions_controller.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index 82eb76ad7..994240a97 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -1,4 +1,6 @@ class Users::SessionsController < Sessions::SessionsController + layout "new_application" + # before_action :configure_sign_in_params, only: [:create] def demo From 624fb13964ebfa630850ae19d35f0348abff3558 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Wed, 14 Jun 2017 14:39:28 +0200 Subject: [PATCH 051/136] Buttons style --- .../stylesheets/new_design/buttons.scss | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 app/assets/stylesheets/new_design/buttons.scss diff --git a/app/assets/stylesheets/new_design/buttons.scss b/app/assets/stylesheets/new_design/buttons.scss new file mode 100644 index 000000000..6a7e8b8b9 --- /dev/null +++ b/app/assets/stylesheets/new_design/buttons.scss @@ -0,0 +1,46 @@ +@import "colors"; + +.button { + display: inline-block; + padding: 8px 16px; + border-radius: 30px; + border: 1px solid $border-grey; + font-size: 14px; + background-color: #FFFFFF; + color: $black; + cursor: pointer; + + &:hover { + background: $light-grey; + text-decoration: none; + } + + &.primary { + color: #FFFFFF; + border-color: $blue; + background-color: $blue; + + &:hover { + background: $light-blue; + } + } + + &.secondary { + color: $blue; + border-color: $blue; + background-color: #FFFFFF; + + &:hover { + background: $light-blue; + } + } + + &.large { + font-size: 18px; + padding: 15px 32px; + } + + &.expand { + width: 100%; + } +} From 90a643d4fbc33cf909f4fac11f9c98ca956efebc Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Wed, 14 Jun 2017 14:42:09 +0200 Subject: [PATCH 052/136] Add /patron page :art: --- app/assets/stylesheets/new_design/patron.scss | 5 +++++ app/controllers/root_controller.rb | 7 ++++++- app/views/root/patron.html.haml | 13 +++++++++++++ config/routes.rb | 2 ++ 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 app/assets/stylesheets/new_design/patron.scss create mode 100644 app/views/root/patron.html.haml diff --git a/app/assets/stylesheets/new_design/patron.scss b/app/assets/stylesheets/new_design/patron.scss new file mode 100644 index 000000000..a0190db63 --- /dev/null +++ b/app/assets/stylesheets/new_design/patron.scss @@ -0,0 +1,5 @@ +@import "placeholders"; + +.patron { + @extend %page-width-container; +} diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb index 6736fc88c..b65437fa7 100644 --- a/app/controllers/root_controller.rb +++ b/app/controllers/root_controller.rb @@ -1,4 +1,6 @@ class RootController < ApplicationController + layout 'new_application' + def index if administrateur_signed_in? return redirect_to admin_procedures_path @@ -26,6 +28,9 @@ class RootController < ApplicationController return redirect_to administrations_path end - render 'landing', :layout => 'new_application' + render 'landing' + end + + def patron end end diff --git a/app/views/root/patron.html.haml b/app/views/root/patron.html.haml new file mode 100644 index 000000000..31ff66a9e --- /dev/null +++ b/app/views/root/patron.html.haml @@ -0,0 +1,13 @@ +.patron + %h1 Patron + + %h2 Boutons + + %p + = link_to ".button", "#", class: "button" + + = link_to ".button.primary", "#", class: "button primary" + + = link_to ".button.large", "#", class: "button large" + + = link_to ".button.large.primary", "#", class: "button large primary" diff --git a/config/routes.rb b/config/routes.rb index 699ba278d..8f58cce09 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -227,5 +227,7 @@ Rails.application.routes.draw do get '/:procedure_path' => '/users/dossiers#commencer' end + get "patron" => "root#patron" + apipie end From 765be88d6dd5007da8476e2fa65055dae69ddb1e Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Wed, 14 Jun 2017 14:57:30 +0200 Subject: [PATCH 053/136] Add expand button on patron --- app/assets/stylesheets/new_design/patron.scss | 4 ++++ app/views/root/patron.html.haml | 3 +++ 2 files changed, 7 insertions(+) diff --git a/app/assets/stylesheets/new_design/patron.scss b/app/assets/stylesheets/new_design/patron.scss index a0190db63..774b980ed 100644 --- a/app/assets/stylesheets/new_design/patron.scss +++ b/app/assets/stylesheets/new_design/patron.scss @@ -2,4 +2,8 @@ .patron { @extend %page-width-container; + + p { + margin-bottom: 20px; + } } diff --git a/app/views/root/patron.html.haml b/app/views/root/patron.html.haml index 31ff66a9e..45b817b64 100644 --- a/app/views/root/patron.html.haml +++ b/app/views/root/patron.html.haml @@ -11,3 +11,6 @@ = link_to ".button.large", "#", class: "button large" = link_to ".button.large.primary", "#", class: "button large primary" + + %p + = link_to ".button.primary.expand", "#", class: "button primary expand" From a82994f00f518a3e66f9a883211bf9c974bdda40 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Wed, 14 Jun 2017 17:39:46 +0200 Subject: [PATCH 054/136] Add inputs & labels to patron --- app/assets/stylesheets/new_design/forms.scss | 27 ++++++++++++++++++++ app/views/root/patron.html.haml | 11 ++++++++ 2 files changed, 38 insertions(+) create mode 100644 app/assets/stylesheets/new_design/forms.scss diff --git a/app/assets/stylesheets/new_design/forms.scss b/app/assets/stylesheets/new_design/forms.scss new file mode 100644 index 000000000..54a11e51b --- /dev/null +++ b/app/assets/stylesheets/new_design/forms.scss @@ -0,0 +1,27 @@ +@import "colors"; + +.form { + label, + input { + font-size: 14px; + } + + label, + input[type=submit] { + margin-top: 24px; + } + + label { + margin-bottom: 8px; + display: inline-block; + } + + input[type=text], + input[type=password] { + display: block; + width: 100%; + border-radius: 4px; + border: solid 1px $border-grey; + padding: 16px; + } +} diff --git a/app/views/root/patron.html.haml b/app/views/root/patron.html.haml index 45b817b64..e02b30641 100644 --- a/app/views/root/patron.html.haml +++ b/app/views/root/patron.html.haml @@ -1,6 +1,17 @@ .patron %h1 Patron + %h2 Formulaires + + %form.form + %label Nom + %input{ type: "text" } + %label Prénom + %input{ type: "text", placeholder: "ex : Ivan" } + %label Mot de passe + %input{ type: "password", value: "12345678" } + %input{ type: "submit", value: "Envoyer" } + %h2 Boutons %p From b68a8b37e8fb34d2d8461384d5545d364a5a6363 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 13 Jun 2017 17:46:08 +0200 Subject: [PATCH 055/136] Redesign the login form --- app/assets/images/login-with-fc-hover.svg | 1 + app/assets/images/login-with-fc.svg | 1 + .../stylesheets/new_design/buttons.scss | 4 + app/assets/stylesheets/new_design/common.scss | 5 + app/assets/stylesheets/new_design/login.scss | 98 +++++++++++++++++++ app/assets/stylesheets/new_design/utils.scss | 4 + app/views/root/patron.html.haml | 2 +- app/views/users/sessions/new.html.haml | 62 +++++++----- spec/features/admin/connection_spec.rb | 4 +- .../upload_piece_justificative_spec.rb | 2 +- .../france_connect_particulier_spec.rb | 10 +- spec/features/users/complete_demande_spec.rb | 8 +- .../drawing_a_zone_with_freedraw_spec.rb | 4 +- spec/features/users/list_dossiers_spec.rb | 2 +- spec/features/users/start_demande_spec.rb | 4 +- .../users/sessions/new.html.haml_spec.rb | 5 +- 16 files changed, 172 insertions(+), 44 deletions(-) create mode 100644 app/assets/images/login-with-fc-hover.svg create mode 100644 app/assets/images/login-with-fc.svg create mode 100644 app/assets/stylesheets/new_design/login.scss diff --git a/app/assets/images/login-with-fc-hover.svg b/app/assets/images/login-with-fc-hover.svg new file mode 100644 index 000000000..e3e0ef572 --- /dev/null +++ b/app/assets/images/login-with-fc-hover.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/login-with-fc.svg b/app/assets/images/login-with-fc.svg new file mode 100644 index 000000000..258e58174 --- /dev/null +++ b/app/assets/images/login-with-fc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/stylesheets/new_design/buttons.scss b/app/assets/stylesheets/new_design/buttons.scss index 6a7e8b8b9..5e0bf38f3 100644 --- a/app/assets/stylesheets/new_design/buttons.scss +++ b/app/assets/stylesheets/new_design/buttons.scss @@ -44,3 +44,7 @@ width: 100%; } } + +.link { + color: $blue; +} diff --git a/app/assets/stylesheets/new_design/common.scss b/app/assets/stylesheets/new_design/common.scss index bbf87837b..2ee0e05d7 100644 --- a/app/assets/stylesheets/new_design/common.scss +++ b/app/assets/stylesheets/new_design/common.scss @@ -5,3 +5,8 @@ body { font-size: 16px; line-height: 1.42857143; } + +h1 { + font-size: 36px; + font-weight: bold; +} diff --git a/app/assets/stylesheets/new_design/login.scss b/app/assets/stylesheets/new_design/login.scss new file mode 100644 index 000000000..12118cc38 --- /dev/null +++ b/app/assets/stylesheets/new_design/login.scss @@ -0,0 +1,98 @@ +@import "colors"; +@import "placeholders"; + +.two-columns { + background: linear-gradient(to right, white 0%, white 50%, $light-grey 50%, $light-grey 100%); +} + +.login-wrapper { + @extend %page-width-container; + display: flex; + flex-direction: row; + align-items: center; +} + +$login-padding: 60px; + +.preview, +.login-form { + width: 50%; + padding: $login-padding; +} + +@media (min-width: $page-width + (2 * $login-padding)) { + .preview { + padding-left: 0; + } + + .login-form { + padding-right: 0; + } +} + +.preview { + font-size: 24px; + + img { + width: 100%; + margin-bottom: 60px; + } + + h3 { + color: $blue; + font-weight: bold; + } +} + +.login-form { + font-size: 14px; + + h1 { + margin-bottom: 20px; + } + + .reset-password { + margin-top: 8px; + } + + .separation { + font-size: 14px; + color: $grey; + margin: 24px 0; + } + + .login-with-fc { + display: inline-block; + height: 52px; + width: 186px; + margin: auto; + margin-bottom: 8px; + background-image: image-url("login-with-fc.svg"); + background-repeat: no-repeat; + background-size: cover; + cursor: pointer; + + &:hover { + background-image: image-url("login-with-fc-hover.svg"); + } + } + + hr { + margin-top: 60px; + margin-bottom: 20px; + background-color: $grey; + border: none; + height: 1px; + } + + .register { + display: flex; + justify-content: space-between; + align-items: center; + + span { + font-size: 18px; + font-weight: bold; + } + } +} diff --git a/app/assets/stylesheets/new_design/utils.scss b/app/assets/stylesheets/new_design/utils.scss index 958f8ff9c..2880cfe30 100644 --- a/app/assets/stylesheets/new_design/utils.scss +++ b/app/assets/stylesheets/new_design/utils.scss @@ -14,6 +14,10 @@ text-align: center; } +.text-right { + text-align: right; +} + .hidden { display: none; } diff --git a/app/views/root/patron.html.haml b/app/views/root/patron.html.haml index e02b30641..d720e004a 100644 --- a/app/views/root/patron.html.haml +++ b/app/views/root/patron.html.haml @@ -10,7 +10,7 @@ %input{ type: "text", placeholder: "ex : Ivan" } %label Mot de passe %input{ type: "password", value: "12345678" } - %input{ type: "submit", value: "Envoyer" } + %input.button{ type: "submit", value: "Envoyer" } %h2 Boutons diff --git a/app/views/users/sessions/new.html.haml b/app/views/users/sessions/new.html.haml index 51ba7cda6..23b9d295d 100644 --- a/app/views/users/sessions/new.html.haml +++ b/app/views/users/sessions/new.html.haml @@ -1,28 +1,44 @@ -#form-login.user_connexion_page - %br - = render partial: 'users/sessions/resume_procedure' +.two-columns + .login-wrapper + .preview + = image_tag "landing/hero/dematerialiser.svg" + .baseline.center + %h3 Un outil simple + %p + pour gérer les formulaires + %br + administratifs dématérialisés. + .login-form + %h1.center Connectez-vous - %h2#login-user - = t('dynamics.users.connexion_title') + = form_for @user, url: user_session_path, html: { class: "form" } do |f| + = f.label :email, "Email" + = f.text_field :email - %a.btn-fc#btn-fcp{ href: '/france_connect/particulier' } - = image_tag 'franceconnect_logo.png' + = f.label :password, "Mot de passe" + = f.password_field :password, value: @user.password, placeholder: "8 caractères minimum" + .reset-password.text-right + = link_to "Mot de passe oublié ?", new_password_path(resource_name), class: "link" - %br - %a.text-info{ href: 'https://fcp.integ01.dev-franceconnect.fr/a-propos', target: '_blank' } - Qu’est-ce que FranceConnect ? - - %hr - - .text-left - #new-user - = simple_form_for @user, url: user_session_path do |f| - = f.input :email - = f.input :password, label: 'Mot de passe', input_html: { value: @user.password } - if devise_mapping.rememberable? - = f.input :remember_me, as: :boolean, label: 'Se souvenir de moi' - .text-center - = f.submit "Se connecter", class: 'btn btn-primary' + = f.check_box :remember_me, as: :boolean + = f.label :remember_me, "Se souvenir de moi" - - if @user.email != DemoEmails[:gestionnaire] && @user.email != DemoEmails[:admin] - = render "users/shared/links" + = f.submit "Se connecter", class: "button large primary expand" + + .separation.center + ou + + .center + = image_tag "login-with-fc-hover.svg", style: "display: none" + = link_to "", france_connect_particulier_path, class: "login-with-fc" + + .center + = link_to "Qu’est-ce que FranceConnect ?", "https://franceconnect.gouv.fr/", target: "_blank", class: "link" + + - if devise_mapping.registerable? + %hr + %p.register + %span + Nouveau sur TPS ? + = link_to "Créer un compte", new_registration_path(resource_name), class: "button" diff --git a/spec/features/admin/connection_spec.rb b/spec/features/admin/connection_spec.rb index 4eb5236e4..bb6205b84 100644 --- a/spec/features/admin/connection_spec.rb +++ b/spec/features/admin/connection_spec.rb @@ -5,8 +5,8 @@ feature 'Administrator connection' do before do visit new_administrateur_session_path end - scenario 'administrator is on admin loggin page' do - expect(page).to have_css('#form-login.user_connexion_page') + scenario 'administrator is on sign in page' do + expect(page).to have_css('#new_user') end context "admin fills form and log in" do diff --git a/spec/features/description_page/upload_piece_justificative_spec.rb b/spec/features/description_page/upload_piece_justificative_spec.rb index bd8cffc8a..25ccf4f75 100644 --- a/spec/features/description_page/upload_piece_justificative_spec.rb +++ b/spec/features/description_page/upload_piece_justificative_spec.rb @@ -9,7 +9,7 @@ feature 'user is on description page' do visit users_dossier_description_path dossier - within('#new-user') do + within('#new_user') do page.find_by_id('user_email').set dossier.user.email page.find_by_id('user_password').set dossier.user.password page.click_on 'Se connecter' diff --git a/spec/features/france_connect/france_connect_particulier_spec.rb b/spec/features/france_connect/france_connect_particulier_spec.rb index 6889ef454..720523714 100644 --- a/spec/features/france_connect/france_connect_particulier_spec.rb +++ b/spec/features/france_connect/france_connect_particulier_spec.rb @@ -25,7 +25,7 @@ feature 'France Connect Particulier Connexion' do end scenario 'link to France Connect is present' do - expect(page).to have_css('a#btn-fcp') + expect(page).to have_css('a.login-with-fc') end context 'and click on france connect link' do @@ -49,7 +49,7 @@ feature 'France Connect Particulier Connexion' do context 'when is the first connexion' do before do - page.find_by_id('btn-fcp').click + page.find('.login-with-fc').click end scenario 'he is redirected to france connect particulier page' do expect(page).to have_content('Nouvelle connexion') @@ -70,7 +70,7 @@ feature 'France Connect Particulier Connexion' do context 'when is not the first connexion' do before do create(:user, france_connect_information: france_connect_information) - page.find_by_id('btn-fcp').click + page.find('.login-with-fc').click end scenario 'he is redirected to user dossiers page' do @@ -83,11 +83,11 @@ feature 'France Connect Particulier Connexion' do before do allow_any_instance_of(FranceConnectParticulierClient).to receive(:authorization_uri).and_return(france_connect_particulier_callback_path(code: code)) allow(FranceConnectService).to receive(:retrieve_user_informations_particulier) { raise Rack::OAuth2::Client::Error.new(500, error: 'Unknown') } - page.find_by_id('btn-fcp').click + page.find('.login-with-fc').click end scenario 'he is redirected to login page' do - expect(page).to have_css('a#btn-fcp') + expect(page).to have_css('a.login-with-fc') end scenario 'error message is displayed' do diff --git a/spec/features/users/complete_demande_spec.rb b/spec/features/users/complete_demande_spec.rb index 2f1eb1d42..26254c31b 100644 --- a/spec/features/users/complete_demande_spec.rb +++ b/spec/features/users/complete_demande_spec.rb @@ -12,14 +12,14 @@ feature 'user path for dossier creation' do end scenario 'he is redirected on login page' do - expect(page).to have_css('#login-user') - expect(page).to have_css('#logo_procedure') - expect(page).to have_css('#titre-procedure') + expect(page).to have_css('#new_user') + expect(page).to have_css('.procedure-logos') + expect(page).to have_content(procedure.libelle) end context 'user sign_in' do before do - within('#new-user') do + within('#new_user') do page.find_by_id('user_email').set user.email page.find_by_id('user_password').set user.password page.click_on 'Se connecter' diff --git a/spec/features/users/drawing_a_zone_with_freedraw_spec.rb b/spec/features/users/drawing_a_zone_with_freedraw_spec.rb index 1ecc5a0ee..e11c7189b 100644 --- a/spec/features/users/drawing_a_zone_with_freedraw_spec.rb +++ b/spec/features/users/drawing_a_zone_with_freedraw_spec.rb @@ -12,11 +12,11 @@ feature 'drawing a zone with freedraw' do end scenario 'he is redirected to login page' do - expect(page).to have_css('#login-user') + expect(page).to have_css('#new_user') end scenario 'he logs in and he is redirected to carte page', vcr: { cassette_name: 'drawing_a_zone_with_freedraw_redirected_to_carte_page' } do - within('#new-user') do + within('#new_user') do page.find_by_id('user_email').set user.email page.find_by_id('user_password').set user.password page.click_on 'Se connecter' diff --git a/spec/features/users/list_dossiers_spec.rb b/spec/features/users/list_dossiers_spec.rb index 7a5236f12..6acdbfb50 100644 --- a/spec/features/users/list_dossiers_spec.rb +++ b/spec/features/users/list_dossiers_spec.rb @@ -11,7 +11,7 @@ describe 'user access to the list of his dossier' do last_updated_dossier.update_column(:updated_at, "19/07/2052 15:35".to_time) visit new_user_session_path - within('#new-user') do + within('#new_user') do page.find_by_id('user_email').set user.email page.find_by_id('user_password').set user.password page.click_on 'Se connecter' diff --git a/spec/features/users/start_demande_spec.rb b/spec/features/users/start_demande_spec.rb index a908412a8..f91c44214 100644 --- a/spec/features/users/start_demande_spec.rb +++ b/spec/features/users/start_demande_spec.rb @@ -11,11 +11,11 @@ feature 'user arrive on siret page' do visit new_users_dossiers_path(procedure_id: procedure.id) end scenario 'he is redirected to login page' do - expect(page).to have_css('#login-user') + expect(page).to have_css('#new_user') end context 'when he enter login information' do before do - within('#new-user') do + within('#new_user') do page.find_by_id('user_email').set user.email page.find_by_id('user_password').set user.password page.click_on 'Se connecter' diff --git a/spec/views/users/sessions/new.html.haml_spec.rb b/spec/views/users/sessions/new.html.haml_spec.rb index cc7880f45..714817335 100644 --- a/spec/views/users/sessions/new.html.haml_spec.rb +++ b/spec/views/users/sessions/new.html.haml_spec.rb @@ -18,8 +18,7 @@ describe 'users/sessions/new.html.haml', type: :view do render end - it { expect(rendered).to have_selector('#form-login #logo_procedure') } - it { expect(rendered).to have_selector('#form-login #titre-procedure') } + it { expect(rendered).to have_selector('.procedure-logos') } it { expect(rendered).to have_content(dossier.procedure.libelle) } it { expect(rendered).to have_content(dossier.procedure.description) } end @@ -29,6 +28,6 @@ describe 'users/sessions/new.html.haml', type: :view do render end - it { expect(rendered).to have_selector('#form-login #logo_tps') } + it { expect(rendered).to have_content('Un outil simple') } end end From fb5c7e7d236476089c87ed43314de4bc743cfb31 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Wed, 14 Jun 2017 18:21:24 +0200 Subject: [PATCH 056/136] Make login responsive --- app/assets/stylesheets/new_design/login.scss | 22 +++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/new_design/login.scss b/app/assets/stylesheets/new_design/login.scss index 12118cc38..fcd7a7977 100644 --- a/app/assets/stylesheets/new_design/login.scss +++ b/app/assets/stylesheets/new_design/login.scss @@ -1,9 +1,8 @@ @import "colors"; @import "placeholders"; +@import "mixins"; -.two-columns { - background: linear-gradient(to right, white 0%, white 50%, $light-grey 50%, $light-grey 100%); -} +$login-breakpoint: 820px; .login-wrapper { @extend %page-width-container; @@ -20,6 +19,23 @@ $login-padding: 60px; padding: $login-padding; } +@media (max-width: $login-breakpoint) { + .preview { + display: none; + } + + .login-form { + @include horizontal-padding(0); + width: 100%; + } +} + +@media (min-width: $login-breakpoint) { + .two-columns { + background: linear-gradient(to right, #FFFFFF 0%, #FFFFFF 50%, $light-grey 50%, $light-grey 100%); + } +} + @media (min-width: $page-width + (2 * $login-padding)) { .preview { padding-left: 0; From 8b9eda0f434ab1d2778ade50d87a0e3e23591050 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Wed, 14 Jun 2017 18:34:53 +0200 Subject: [PATCH 057/136] No login link on header on login page --- app/views/layouts/_new_header.haml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/layouts/_new_header.haml b/app/views/layouts/_new_header.haml index 6e656bbc4..fc6e50e9e 100644 --- a/app/views/layouts/_new_header.haml +++ b/app/views/layouts/_new_header.haml @@ -3,4 +3,5 @@ = link_to root_path do %img.header-logo{ src: image_url("header/logo-tps.svg") } - = link_to "Connexion", new_user_session_path, class: "header-login-button" + - unless request.path == new_user_session_path + = link_to "Connexion", new_user_session_path, class: "header-login-button" From c944088076f4cf9b7a28ec2a6b647051c6382e38 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Thu, 15 Jun 2017 18:08:13 +0200 Subject: [PATCH 058/136] Add procedure overview on login --- app/assets/images/flag_of_europe.svg | 1 + app/assets/stylesheets/new_design/login.scss | 25 ++++++++++++++++- app/views/users/sessions/new.html.haml | 28 +++++++++++++++----- 3 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 app/assets/images/flag_of_europe.svg diff --git a/app/assets/images/flag_of_europe.svg b/app/assets/images/flag_of_europe.svg new file mode 100644 index 000000000..754f1480f --- /dev/null +++ b/app/assets/images/flag_of_europe.svg @@ -0,0 +1 @@ +European flag \ No newline at end of file diff --git a/app/assets/stylesheets/new_design/login.scss b/app/assets/stylesheets/new_design/login.scss index fcd7a7977..af3917862 100644 --- a/app/assets/stylesheets/new_design/login.scss +++ b/app/assets/stylesheets/new_design/login.scss @@ -49,7 +49,7 @@ $login-padding: 60px; .preview { font-size: 24px; - img { + .paperless-logo { width: 100%; margin-bottom: 60px; } @@ -58,6 +58,29 @@ $login-padding: 60px; color: $blue; font-weight: bold; } + + .close-procedure { + font-size: 12px; + } + + .procedure-title { + font-size: 30px; + margin: 50px 0 32px; + } + + .procedure-description { + font-size: 14px; + } + + .procedure-logos { + display: flex; + justify-content: space-around; + + img { + max-height: 130px; + margin: 0 10px; + } + } } .login-form { diff --git a/app/views/users/sessions/new.html.haml b/app/views/users/sessions/new.html.haml index 23b9d295d..84ae32d70 100644 --- a/app/views/users/sessions/new.html.haml +++ b/app/views/users/sessions/new.html.haml @@ -1,13 +1,27 @@ .two-columns .login-wrapper .preview - = image_tag "landing/hero/dematerialiser.svg" - .baseline.center - %h3 Un outil simple - %p - pour gérer les formulaires - %br - administratifs dématérialisés. + - unless @dossier + = image_tag "landing/hero/dematerialiser.svg", class: "paperless-logo" + .baseline.center + %h3 Un outil simple + %p + pour gérer les formulaires + %br + administratifs dématérialisés. + - else + .text-right + = link_to "Fermer", users_no_procedure_url, class: "link close-procedure" + .procedure-logos + = image_tag @dossier.procedure.decorate.logo_img + - if @dossier.procedure.euro_flag + = image_tag "flag_of_europe.svg" + + %h2.procedure-title + = @dossier.procedure.libelle + %p.procedure-description + = h @dossier.procedure.description.html_safe + .login-form %h1.center Connectez-vous From 24aaa5352125edbb0547282c1524d1f1581fe84a Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 19 Jun 2017 18:31:21 +0200 Subject: [PATCH 059/136] Add the parallelism key to the CircleCI conf file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s now required --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index c44ceede5..c27c968f1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,7 @@ version: 2 jobs: build: + parallelism: 2 docker: - image: ruby:2.3.1 - image: postgres:9.4.1 From 2e795fb2a529a90c440224845b418042511e7772 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 20 Jun 2017 09:51:18 +0200 Subject: [PATCH 060/136] single quote => double quotes --- app/views/backoffice/avis/sign_up.html.haml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/backoffice/avis/sign_up.html.haml b/app/views/backoffice/avis/sign_up.html.haml index faeb3dadb..9aa3c5bcc 100644 --- a/app/views/backoffice/avis/sign_up.html.haml +++ b/app/views/backoffice/avis/sign_up.html.haml @@ -5,11 +5,11 @@ .right %h1 Créez-vous un compte - = form_for(Gestionnaire.new, url: { controller: 'backoffice/avis', action: :create_gestionnaire }, method: :post) do |f| - = f.label :email, 'Email' + = form_for(Gestionnaire.new, url: { controller: "backoffice/avis", action: :create_gestionnaire }, method: :post) do |f| + = f.label :email, "Email" = f.email_field :email, value: @email, disabled: true - = f.label :password, 'Mot de passe' - = f.password_field :password, autofocus: true, required: true, placeholder: '8 caractères minimum' + = f.label :password, "Mot de passe" + = f.password_field :password, autofocus: true, required: true, placeholder: "8 caractères minimum" %button Créer un compte From f74462d826d0435bd4d83987f437a8a2c7e26240 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 20 Jun 2017 10:03:49 +0200 Subject: [PATCH 061/136] Use generic css for avis_sign_up --- .../stylesheets/new_design/avis_sign_up.scss | 46 ------------------- app/assets/stylesheets/new_design/forms.scss | 5 ++ app/views/backoffice/avis/sign_up.html.haml | 4 +- 3 files changed, 7 insertions(+), 48 deletions(-) diff --git a/app/assets/stylesheets/new_design/avis_sign_up.scss b/app/assets/stylesheets/new_design/avis_sign_up.scss index 9c94d35af..9c4d414b9 100644 --- a/app/assets/stylesheets/new_design/avis_sign_up.scss +++ b/app/assets/stylesheets/new_design/avis_sign_up.scss @@ -41,51 +41,5 @@ form { max-width: 420px; } - - label, - input { - display: block; - width: 100%; - } - - label { - font-size: 14px; - line-height: 1.57; - margin: 24px 0 8px; - } - - input { - border: solid 1px $border-grey; - border-radius: 4px; - height: 56px; - padding: 0 15px; - font-family: Muli; - font-size: 14px; - - &:disabled { - background-color: $border-grey; - } - } - - button { - display: inline-block; - height: 60px; - line-height: 60px; - border: none; - border-radius: 60px; - background-color: $blue; - color: #FFFFFF; - font-size: 16px; - text-align: center; - width: 100%; - margin: 55px 0; - - &:hover { - color: #FFFFFF; - text-decoration: none; - background-color: $light-blue; - cursor: pointer; - } - } } } diff --git a/app/assets/stylesheets/new_design/forms.scss b/app/assets/stylesheets/new_design/forms.scss index 54a11e51b..d3a848c77 100644 --- a/app/assets/stylesheets/new_design/forms.scss +++ b/app/assets/stylesheets/new_design/forms.scss @@ -17,11 +17,16 @@ } input[type=text], + input[type=email], input[type=password] { display: block; width: 100%; border-radius: 4px; border: solid 1px $border-grey; padding: 16px; + + &:disabled { + background-color: $border-grey; + } } } diff --git a/app/views/backoffice/avis/sign_up.html.haml b/app/views/backoffice/avis/sign_up.html.haml index 9aa3c5bcc..71729679d 100644 --- a/app/views/backoffice/avis/sign_up.html.haml +++ b/app/views/backoffice/avis/sign_up.html.haml @@ -5,11 +5,11 @@ .right %h1 Créez-vous un compte - = form_for(Gestionnaire.new, url: { controller: "backoffice/avis", action: :create_gestionnaire }, method: :post) do |f| + = form_for(Gestionnaire.new, url: { controller: "backoffice/avis", action: :create_gestionnaire }, method: :post, html: { class: "form" }) do |f| = f.label :email, "Email" = f.email_field :email, value: @email, disabled: true = f.label :password, "Mot de passe" = f.password_field :password, autofocus: true, required: true, placeholder: "8 caractères minimum" - %button Créer un compte + = f.submit "Créer un compte", class: "button large primary expand" From a69915d8b5d2f8e2173494e60c95be06cdc7028b Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 20 Jun 2017 10:09:19 +0200 Subject: [PATCH 062/136] standardize form title --- app/assets/stylesheets/new_design/avis_sign_up.scss | 6 ------ app/assets/stylesheets/new_design/forms.scss | 5 +++++ app/assets/stylesheets/new_design/login.scss | 4 ---- app/views/backoffice/avis/sign_up.html.haml | 4 ++-- app/views/users/sessions/new.html.haml | 4 ++-- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/app/assets/stylesheets/new_design/avis_sign_up.scss b/app/assets/stylesheets/new_design/avis_sign_up.scss index 9c4d414b9..9fdbe54a2 100644 --- a/app/assets/stylesheets/new_design/avis_sign_up.scss +++ b/app/assets/stylesheets/new_design/avis_sign_up.scss @@ -32,12 +32,6 @@ .right { background-color: $light-grey; - h1 { - font-size: 36px; - font-weight: bold; - margin-bottom: 60px; - } - form { max-width: 420px; } diff --git a/app/assets/stylesheets/new_design/forms.scss b/app/assets/stylesheets/new_design/forms.scss index d3a848c77..e1616d1e7 100644 --- a/app/assets/stylesheets/new_design/forms.scss +++ b/app/assets/stylesheets/new_design/forms.scss @@ -1,6 +1,11 @@ @import "colors"; .form { + h1 { + text-align: center; + margin-bottom: 20px; + } + label, input { font-size: 14px; diff --git a/app/assets/stylesheets/new_design/login.scss b/app/assets/stylesheets/new_design/login.scss index af3917862..5270561c1 100644 --- a/app/assets/stylesheets/new_design/login.scss +++ b/app/assets/stylesheets/new_design/login.scss @@ -86,10 +86,6 @@ $login-padding: 60px; .login-form { font-size: 14px; - h1 { - margin-bottom: 20px; - } - .reset-password { margin-top: 8px; } diff --git a/app/views/backoffice/avis/sign_up.html.haml b/app/views/backoffice/avis/sign_up.html.haml index 71729679d..9efacd696 100644 --- a/app/views/backoffice/avis/sign_up.html.haml +++ b/app/views/backoffice/avis/sign_up.html.haml @@ -3,9 +3,9 @@ %p.description= @dossier.procedure.libelle %p.dossier Dossier nº #{@dossier.id} .right - %h1 Créez-vous un compte - = form_for(Gestionnaire.new, url: { controller: "backoffice/avis", action: :create_gestionnaire }, method: :post, html: { class: "form" }) do |f| + %h1 Créez-vous un compte + = f.label :email, "Email" = f.email_field :email, value: @email, disabled: true diff --git a/app/views/users/sessions/new.html.haml b/app/views/users/sessions/new.html.haml index 84ae32d70..342750917 100644 --- a/app/views/users/sessions/new.html.haml +++ b/app/views/users/sessions/new.html.haml @@ -23,9 +23,9 @@ = h @dossier.procedure.description.html_safe .login-form - %h1.center Connectez-vous - = form_for @user, url: user_session_path, html: { class: "form" } do |f| + %h1 Connectez-vous + = f.label :email, "Email" = f.text_field :email From 3388dc0e1ffcdb6e609490a008c5382a95fe417c Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 20 Jun 2017 11:13:24 +0200 Subject: [PATCH 063/136] Create generic two-columns layout --- .../stylesheets/new_design/layouts.scss | 32 +++++++++++++ app/assets/stylesheets/new_design/patron.scss | 4 +- app/views/root/patron.html.haml | 46 +++++++++++-------- 3 files changed, 63 insertions(+), 19 deletions(-) create mode 100644 app/assets/stylesheets/new_design/layouts.scss diff --git a/app/assets/stylesheets/new_design/layouts.scss b/app/assets/stylesheets/new_design/layouts.scss new file mode 100644 index 000000000..98fcfe043 --- /dev/null +++ b/app/assets/stylesheets/new_design/layouts.scss @@ -0,0 +1,32 @@ +@import "colors"; +@import "constants"; +@import "placeholders"; + +.two-columns { + $column-padding: 60px; + $two-columns-breakpoint: $page-width + (2 * $column-padding); + + background: linear-gradient(to right, #FFFFFF 0%, #FFFFFF 50%, $light-grey 50%, $light-grey 100%); + + .columns-container { + @extend %page-width-container; + display: flex; + flex-direction: row; + align-items: center; + } + + .column { + width: 50%; + padding: $column-padding; + + @media (min-width: $two-columns-breakpoint) { + &:first-child { + padding-left: 0; + } + + &:last-child { + padding-right: 0; + } + } + } +} diff --git a/app/assets/stylesheets/new_design/patron.scss b/app/assets/stylesheets/new_design/patron.scss index 774b980ed..54bc0e0d3 100644 --- a/app/assets/stylesheets/new_design/patron.scss +++ b/app/assets/stylesheets/new_design/patron.scss @@ -1,7 +1,9 @@ @import "placeholders"; .patron { - @extend %page-width-container; + .patron-container { + @extend %page-width-container; + } p { margin-bottom: 20px; diff --git a/app/views/root/patron.html.haml b/app/views/root/patron.html.haml index d720e004a..a219a9da9 100644 --- a/app/views/root/patron.html.haml +++ b/app/views/root/patron.html.haml @@ -1,27 +1,37 @@ .patron - %h1 Patron + .patron-container + %h1 Patron - %h2 Formulaires + %h2 Formulaires - %form.form - %label Nom - %input{ type: "text" } - %label Prénom - %input{ type: "text", placeholder: "ex : Ivan" } - %label Mot de passe - %input{ type: "password", value: "12345678" } - %input.button{ type: "submit", value: "Envoyer" } + %form.form + %label Nom + %input{ type: "text" } + %label Prénom + %input{ type: "text", placeholder: "ex : Ivan" } + %label Mot de passe + %input{ type: "password", value: "12345678" } + %input.button{ type: "submit", value: "Envoyer" } - %h2 Boutons + %h2 Boutons - %p - = link_to ".button", "#", class: "button" + %p + = link_to ".button", "#", class: "button" - = link_to ".button.primary", "#", class: "button primary" + = link_to ".button.primary", "#", class: "button primary" - = link_to ".button.large", "#", class: "button large" + = link_to ".button.large", "#", class: "button large" - = link_to ".button.large.primary", "#", class: "button large primary" + = link_to ".button.large.primary", "#", class: "button large primary" - %p - = link_to ".button.primary.expand", "#", class: "button primary expand" + %p + = link_to ".button.primary.expand", "#", class: "button primary expand" + + %h2 Layout deux colonnes + + .two-columns + .columns-container + .column + Insérer ici le contenu de la colonne 1 + .column + Insérer ici le contenu de la colonne 2 From 3f20d6fb9de569586437dfc0d1055710e4a42944 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 20 Jun 2017 11:42:54 +0200 Subject: [PATCH 064/136] login should use generic two columns layout --- app/assets/stylesheets/new_design/login.scss | 31 ++------------------ app/views/users/sessions/new.html.haml | 6 ++-- 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/app/assets/stylesheets/new_design/login.scss b/app/assets/stylesheets/new_design/login.scss index 5270561c1..0d211adf1 100644 --- a/app/assets/stylesheets/new_design/login.scss +++ b/app/assets/stylesheets/new_design/login.scss @@ -4,45 +4,20 @@ $login-breakpoint: 820px; -.login-wrapper { - @extend %page-width-container; - display: flex; - flex-direction: row; - align-items: center; -} - -$login-padding: 60px; - -.preview, -.login-form { - width: 50%; - padding: $login-padding; -} - @media (max-width: $login-breakpoint) { .preview { display: none; } - .login-form { + .two-columns .column.login-form { @include horizontal-padding(0); width: 100%; } } -@media (min-width: $login-breakpoint) { +@media (max-width: $login-breakpoint) { .two-columns { - background: linear-gradient(to right, #FFFFFF 0%, #FFFFFF 50%, $light-grey 50%, $light-grey 100%); - } -} - -@media (min-width: $page-width + (2 * $login-padding)) { - .preview { - padding-left: 0; - } - - .login-form { - padding-right: 0; + background: #FFFFFF; } } diff --git a/app/views/users/sessions/new.html.haml b/app/views/users/sessions/new.html.haml index 342750917..e13082c7a 100644 --- a/app/views/users/sessions/new.html.haml +++ b/app/views/users/sessions/new.html.haml @@ -1,6 +1,6 @@ .two-columns - .login-wrapper - .preview + .columns-container + .column.preview - unless @dossier = image_tag "landing/hero/dematerialiser.svg", class: "paperless-logo" .baseline.center @@ -22,7 +22,7 @@ %p.procedure-description = h @dossier.procedure.description.html_safe - .login-form + .column.login-form = form_for @user, url: user_session_path, html: { class: "form" } do |f| %h1 Connectez-vous From 482002fe7920de1ff149766c57deeb713556e416 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 20 Jun 2017 13:50:20 +0200 Subject: [PATCH 065/136] Avis sign up should use generic layout --- .../stylesheets/new_design/avis_sign_up.scss | 16 ------------ app/assets/stylesheets/new_design/login.scss | 2 +- app/views/backoffice/avis/sign_up.html.haml | 25 ++++++++++--------- app/views/users/sessions/new.html.haml | 2 +- 4 files changed, 15 insertions(+), 30 deletions(-) diff --git a/app/assets/stylesheets/new_design/avis_sign_up.scss b/app/assets/stylesheets/new_design/avis_sign_up.scss index 9fdbe54a2..f202205c9 100644 --- a/app/assets/stylesheets/new_design/avis_sign_up.scss +++ b/app/assets/stylesheets/new_design/avis_sign_up.scss @@ -2,14 +2,6 @@ @import "colors"; .avis-sign-up { - display: flex; - - .left, - .right { - width: 50%; - padding: 60px 86px; - } - .left { p { margin: auto; @@ -28,12 +20,4 @@ margin-top: 15px; } } - - .right { - background-color: $light-grey; - - form { - max-width: 420px; - } - } } diff --git a/app/assets/stylesheets/new_design/login.scss b/app/assets/stylesheets/new_design/login.scss index 0d211adf1..109c7b675 100644 --- a/app/assets/stylesheets/new_design/login.scss +++ b/app/assets/stylesheets/new_design/login.scss @@ -16,7 +16,7 @@ $login-breakpoint: 820px; } @media (max-width: $login-breakpoint) { - .two-columns { + .two-columns.login { background: #FFFFFF; } } diff --git a/app/views/backoffice/avis/sign_up.html.haml b/app/views/backoffice/avis/sign_up.html.haml index 9efacd696..c14b92040 100644 --- a/app/views/backoffice/avis/sign_up.html.haml +++ b/app/views/backoffice/avis/sign_up.html.haml @@ -1,15 +1,16 @@ -.avis-sign-up - .left - %p.description= @dossier.procedure.libelle - %p.dossier Dossier nº #{@dossier.id} - .right - = form_for(Gestionnaire.new, url: { controller: "backoffice/avis", action: :create_gestionnaire }, method: :post, html: { class: "form" }) do |f| - %h1 Créez-vous un compte +.two-columns.avis-sign-up + .columns-container + .column.left + %p.description= @dossier.procedure.libelle + %p.dossier Dossier nº #{@dossier.id} + .column + = form_for(Gestionnaire.new, url: { controller: "backoffice/avis", action: :create_gestionnaire }, method: :post, html: { class: "form" }) do |f| + %h1 Créez-vous un compte - = f.label :email, "Email" - = f.email_field :email, value: @email, disabled: true + = f.label :email, "Email" + = f.email_field :email, value: @email, disabled: true - = f.label :password, "Mot de passe" - = f.password_field :password, autofocus: true, required: true, placeholder: "8 caractères minimum" + = f.label :password, "Mot de passe" + = f.password_field :password, autofocus: true, required: true, placeholder: "8 caractères minimum" - = f.submit "Créer un compte", class: "button large primary expand" + = f.submit "Créer un compte", class: "button large primary expand" diff --git a/app/views/users/sessions/new.html.haml b/app/views/users/sessions/new.html.haml index e13082c7a..7f399eeec 100644 --- a/app/views/users/sessions/new.html.haml +++ b/app/views/users/sessions/new.html.haml @@ -1,4 +1,4 @@ -.two-columns +.two-columns.login .columns-container .column.preview - unless @dossier From 58350f219a1f4c69c180cdd0c5aad82996eb0bdd Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 20 Jun 2017 15:55:17 +0200 Subject: [PATCH 066/136] Rewrite tests that should fail so that they actually do --- spec/controllers/administrateur/sessions_controller_spec.rb | 4 +++- spec/controllers/gestionnaires/sessions_controller_spec.rb | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/controllers/administrateur/sessions_controller_spec.rb b/spec/controllers/administrateur/sessions_controller_spec.rb index 5eb92ecf0..e0fe7d5c5 100644 --- a/spec/controllers/administrateur/sessions_controller_spec.rb +++ b/spec/controllers/administrateur/sessions_controller_spec.rb @@ -7,6 +7,7 @@ describe Administrateurs::SessionsController, type: :controller do describe '.demo' do subject { get :demo } + render_views context 'when rails env is production' do before do @@ -20,8 +21,9 @@ describe Administrateurs::SessionsController, type: :controller do it { expect(subject.status).to eq 200 } it 'Administrateur demo is initiated' do - expect(User).to receive(:new).with(email: 'admin@tps.fr', password: 'password').and_return(Administrateur) subject + expect(response.body).to have_css("input#user_email[value='admin@tps.fr']") + expect(response.body).to have_css("input#user_password[value='password']") end end end diff --git a/spec/controllers/gestionnaires/sessions_controller_spec.rb b/spec/controllers/gestionnaires/sessions_controller_spec.rb index 6266e429f..9f8bc37d0 100644 --- a/spec/controllers/gestionnaires/sessions_controller_spec.rb +++ b/spec/controllers/gestionnaires/sessions_controller_spec.rb @@ -7,6 +7,7 @@ describe Gestionnaires::SessionsController, type: :controller do describe '.demo' do subject { get :demo } + render_views context 'when rails env is production' do before do @@ -20,8 +21,9 @@ describe Gestionnaires::SessionsController, type: :controller do it { expect(subject.status).to eq 200 } it 'Gestionnaire demo is initiated' do - expect(User).to receive(:new).with(email: 'gestionnaire@apientreprise.fr', password: 'password').and_return(Gestionnaire) subject + expect(response.body).to have_css("input#user_email[value='gestionnaire@apientreprise.fr']") + expect(response.body).to have_css("input#user_password[value='password']") end end end From e51d8965a2376cebe068167ebdead2498673b411 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 20 Jun 2017 14:39:56 +0200 Subject: [PATCH 067/136] [Fix #468] fix demo login --- app/controllers/administrateurs/sessions_controller.rb | 2 ++ app/controllers/gestionnaires/sessions_controller.rb | 2 ++ app/views/users/sessions/new.html.haml | 8 +++++--- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/controllers/administrateurs/sessions_controller.rb b/app/controllers/administrateurs/sessions_controller.rb index be676864f..3c8328e30 100644 --- a/app/controllers/administrateurs/sessions_controller.rb +++ b/app/controllers/administrateurs/sessions_controller.rb @@ -1,4 +1,6 @@ class Administrateurs::SessionsController < Sessions::SessionsController + layout "new_application" + def demo return redirect_to root_path if Rails.env.production? diff --git a/app/controllers/gestionnaires/sessions_controller.rb b/app/controllers/gestionnaires/sessions_controller.rb index c0f7d7b9b..326cf2200 100644 --- a/app/controllers/gestionnaires/sessions_controller.rb +++ b/app/controllers/gestionnaires/sessions_controller.rb @@ -1,4 +1,6 @@ class Gestionnaires::SessionsController < Sessions::SessionsController + layout "new_application" + def demo return redirect_to root_path if Rails.env.production? diff --git a/app/views/users/sessions/new.html.haml b/app/views/users/sessions/new.html.haml index 7f399eeec..93e0aa480 100644 --- a/app/views/users/sessions/new.html.haml +++ b/app/views/users/sessions/new.html.haml @@ -31,8 +31,10 @@ = f.label :password, "Mot de passe" = f.password_field :password, value: @user.password, placeholder: "8 caractères minimum" - .reset-password.text-right - = link_to "Mot de passe oublié ?", new_password_path(resource_name), class: "link" + + - if [:user, :gestionnaire].include?(resource_name) + .reset-password.text-right + = link_to "Mot de passe oublié ?", new_password_path(resource_name), class: "link" - if devise_mapping.rememberable? = f.check_box :remember_me, as: :boolean @@ -50,7 +52,7 @@ .center = link_to "Qu’est-ce que FranceConnect ?", "https://franceconnect.gouv.fr/", target: "_blank", class: "link" - - if devise_mapping.registerable? + - if resource_name == :user %hr %p.register %span From 250b858128717d1691899f8b5c174f665ce1a938 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 20 Jun 2017 17:31:58 +0200 Subject: [PATCH 068/136] json style for hashes --- app/views/layouts/new_application.html.haml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/views/layouts/new_application.html.haml b/app/views/layouts/new_application.html.haml index 19c5bc700..3e31bd2f7 100644 --- a/app/views/layouts/new_application.html.haml +++ b/app/views/layouts/new_application.html.haml @@ -1,8 +1,8 @@ %html %head - %meta{ "http-equiv" => "Content-Type", :content => "text/html; charset=UTF-8" } - %meta{ "http-equiv" => "X-UA-Compatible", :content => "IE=edge" } - %meta{ :name => "turbolinks-cache-control", :content => "no-cache" } + %meta{ "http-equiv": "Content-Type", content: "text/html; charset=UTF-8" } + %meta{ "http-equiv": "X-UA-Compatible", content: "IE=edge" } + %meta{ name: "turbolinks-cache-control", content: "no-cache" } %meta{ name: "viewport", content: "width=device-width, initial-scale=1" } = csrf_meta_tags = action_cable_meta_tag @@ -14,12 +14,12 @@ = favicon_link_tag(image_url("favicons/32x32.png"), type: "image/png", sizes: "32x32") = favicon_link_tag(image_url("favicons/96x96.png"), type: "image/png", sizes: "96x96") - = stylesheet_link_tag "new_design/new_application", :media => "all", "data-turbolinks-track" => true - = stylesheet_link_tag "print", :media => "print", "data-turbolinks-track" => true + = stylesheet_link_tag "new_design/new_application", media: "all", "data-turbolinks-track": true + = stylesheet_link_tag "print", media: "print", "data-turbolinks-track": true %body - = render :partial => "layouts/support_navigator_banner" - = render :partial => "layouts/ie_lt_10" + = render partial: "layouts/support_navigator_banner" + = render partial: "layouts/ie_lt_10" #beta{ class:(Rails.env == "production" ? "" : "beta_staging") } - if Rails.env == "production" Beta @@ -30,14 +30,14 @@ = render partial: "layouts/flash_messages" = yield - = render :partial => "layouts/switch_devise_profile_module" + = render partial: "layouts/switch_devise_profile_module" = render partial: "layouts/new_footer" = render partial: "layouts/google_analytics" = render partial: "layouts/mailjet_newsletter" - = javascript_include_tag "application", "data-turbolinks-track" => true + = javascript_include_tag "application", "data-turbolinks-track": true = yield :charts_js - if Rails.env == "test" - %script{ :type => "text/javascript" } + %script{ type: "text/javascript" } (typeof jQuery !== "undefined") && (jQuery.fx.off = true); From f1ab350897d63e30ba0a23e758cc00dccd16d5cb Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 20 Jun 2017 17:34:48 +0200 Subject: [PATCH 069/136] fix turbolinks bug when switching between layouts --- app/views/layouts/application.html.haml | 4 ++-- app/views/layouts/new_application.html.haml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index d4278bc3d..67b890749 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -10,8 +10,8 @@ = favicon_link_tag(image_url("favicons/32x32.png"), type: "image/png", sizes: "32x32") = favicon_link_tag(image_url("favicons/96x96.png"), type: "image/png", sizes: "96x96") - = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true - = stylesheet_link_tag 'print', media: 'print', 'data-turbolinks-track' => true + = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => "reload" + = stylesheet_link_tag 'print', media: 'print', 'data-turbolinks-track' => "reload" = javascript_include_tag 'application', 'data-turbolinks-track' => true = csrf_meta_tags diff --git a/app/views/layouts/new_application.html.haml b/app/views/layouts/new_application.html.haml index 3e31bd2f7..e0e9f4dce 100644 --- a/app/views/layouts/new_application.html.haml +++ b/app/views/layouts/new_application.html.haml @@ -14,7 +14,7 @@ = favicon_link_tag(image_url("favicons/32x32.png"), type: "image/png", sizes: "32x32") = favicon_link_tag(image_url("favicons/96x96.png"), type: "image/png", sizes: "96x96") - = stylesheet_link_tag "new_design/new_application", media: "all", "data-turbolinks-track": true + = stylesheet_link_tag "new_design/new_application", media: "all", "data-turbolinks-track": "reload" = stylesheet_link_tag "print", media: "print", "data-turbolinks-track": true %body From 6777b51331bad235aaf7e4d81f87e3e07dd8838b Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Wed, 21 Jun 2017 17:36:27 +0200 Subject: [PATCH 070/136] rename login css to auth, since it will also be used by sign up --- .../stylesheets/new_design/{login.scss => auth.scss} | 12 ++++++------ app/views/users/sessions/new.html.haml | 4 ++-- .../france_connect/particulier_controller_spec.rb | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) rename app/assets/stylesheets/new_design/{login.scss => auth.scss} (88%) diff --git a/app/assets/stylesheets/new_design/login.scss b/app/assets/stylesheets/new_design/auth.scss similarity index 88% rename from app/assets/stylesheets/new_design/login.scss rename to app/assets/stylesheets/new_design/auth.scss index 109c7b675..6f292b928 100644 --- a/app/assets/stylesheets/new_design/login.scss +++ b/app/assets/stylesheets/new_design/auth.scss @@ -2,21 +2,21 @@ @import "placeholders"; @import "mixins"; -$login-breakpoint: 820px; +$auth-breakpoint: 820px; -@media (max-width: $login-breakpoint) { +@media (max-width: $auth-breakpoint) { .preview { display: none; } - .two-columns .column.login-form { + .two-columns .column.auth-form { @include horizontal-padding(0); width: 100%; } } -@media (max-width: $login-breakpoint) { - .two-columns.login { +@media (max-width: $auth-breakpoint) { + .two-columns.auth { background: #FFFFFF; } } @@ -58,7 +58,7 @@ $login-breakpoint: 820px; } } -.login-form { +.auth-form { font-size: 14px; .reset-password { diff --git a/app/views/users/sessions/new.html.haml b/app/views/users/sessions/new.html.haml index 93e0aa480..ffac4fae9 100644 --- a/app/views/users/sessions/new.html.haml +++ b/app/views/users/sessions/new.html.haml @@ -1,4 +1,4 @@ -.two-columns.login +.two-columns.auth .columns-container .column.preview - unless @dossier @@ -22,7 +22,7 @@ %p.procedure-description = h @dossier.procedure.description.html_safe - .column.login-form + .column.auth-form = form_for @user, url: user_session_path, html: { class: "form" } do |f| %h1 Connectez-vous diff --git a/spec/controllers/france_connect/particulier_controller_spec.rb b/spec/controllers/france_connect/particulier_controller_spec.rb index c0a6857b3..279f7b9ef 100644 --- a/spec/controllers/france_connect/particulier_controller_spec.rb +++ b/spec/controllers/france_connect/particulier_controller_spec.rb @@ -13,7 +13,7 @@ describe FranceConnect::ParticulierController, type: :controller do let(:user_info) { Hashie::Mash.new(france_connect_particulier_id: france_connect_particulier_id, given_name: given_name, family_name: family_name, birthdate: birthdate, birthplace: birthplace, gender: gender, email: email, password: password) } - describe '.login' do + describe '.auth' do it 'redirect to france connect serveur' do get :login expect(response.status).to eq(302) From ac5c00c26f0004eb2a26020df5225c1e0b6acd5a Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Wed, 21 Jun 2017 14:20:22 +0200 Subject: [PATCH 071/136] [fix #452] redesign sign up form --- .../users/registrations_controller.rb | 2 + app/views/users/registrations/new.html.haml | 91 ++++++------------- .../users/registrations_controller_spec.rb | 8 +- 3 files changed, 35 insertions(+), 66 deletions(-) diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb index 1a231b900..3de89b1cd 100644 --- a/app/controllers/users/registrations_controller.rb +++ b/app/controllers/users/registrations_controller.rb @@ -1,4 +1,6 @@ class Users::RegistrationsController < Devise::RegistrationsController + layout "new_application" + # before_action :configure_sign_up_params, only: [:create] # before_action :configure_account_update_params, only: [:update] diff --git a/app/views/users/registrations/new.html.haml b/app/views/users/registrations/new.html.haml index a81def70b..fcaf254d6 100644 --- a/app/views/users/registrations/new.html.haml +++ b/app/views/users/registrations/new.html.haml @@ -1,64 +1,33 @@ --#

Sign up

--# --# <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> --# <%= devise_error_messages! %> --# --#
--# <%= f.label :email %>
--# <%= f.email_field :email, autofocus: true %> --#
--# --#
--# <%= f.label :password %> --# <% if @validatable %> --# (<%= @minimum_password_length %> characters minimum) --# <% end %>
--# <%= f.password_field :password, autocomplete: "off" %> --#
--# --#
--# <%= f.label :password_confirmation %>
--# <%= f.password_field :password_confirmation, autocomplete: "off" %> --#
--# --#
--# <%= f.submit "Sign up" %> --#
--# <% end %> +.two-columns.auth + .columns-container + .column.preview + = image_tag "landing/hero/dematerialiser.svg", class: "paperless-logo" + .baseline.center + %h3 Un outil simple + %p + pour gérer les formulaires + %br + administratifs dématérialisés. -= devise_error_messages! + .column.auth-form + = devise_error_messages! + = form_for User.new, url: user_registration_path, html: { class: "form" } do |f| + %h1 Créez-vous un compte -#form-login - %br - .flag - = image_tag(image_url(LOGO_NAME)) - %br - %h2#gestionnaire_login Inscription + = f.label :email, "Email" + = f.text_field :email - %br - %br - #new-user - = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| - %h4 - = f.label :email - .input-group - .input-group-addon - %span.fa.fa-user - = f.email_field :email, class: 'form-control', placeholder: 'Email', value: params[:user_email] - %br - %h4 - = f.label :password, 'Mot de passe' - .input-group - .input-group-addon - %span.fa.fa-asterisk - = f.password_field :password, autocomplete: "off", class: 'form-control', placeholder: 'Mot de passe' - %br - .input-group - .input-group-addon - %span.fa.fa-asterisk - = f.password_field :password_confirmation, autocomplete: "off", class: 'form-control', placeholder: 'Répeter le mot de passe' - %br - %br - .actions - = f.submit "S'enregistrer", class: 'btn btn-primary' - %br + = f.label :password, "Mot de passe" + = f.password_field :password, value: @user.password, placeholder: "8 caractères minimum" + + = f.submit "Créer un compte", class: "button large primary expand" + + .separation.center + ou + + .center + = image_tag "login-with-fc-hover.svg", style: "display: none" + = link_to "", france_connect_particulier_path, class: "login-with-fc" + + .center + = link_to "Qu’est-ce que FranceConnect ?", "https://franceconnect.gouv.fr/", target: "_blank", class: "link" diff --git a/spec/controllers/users/registrations_controller_spec.rb b/spec/controllers/users/registrations_controller_spec.rb index 324995ca3..dd7d1add8 100644 --- a/spec/controllers/users/registrations_controller_spec.rb +++ b/spec/controllers/users/registrations_controller_spec.rb @@ -4,18 +4,16 @@ describe Users::RegistrationsController, type: :controller do let(:email) { 'test@octo.com' } let(:password) { 'password' } - let(:user) { {email: email, password: password, password_confirmation: password} } + let(:user) { { email: email, password: password } } before do @request.env["devise.mapping"] = Devise.mappings[:user] end describe '.create' do - subject { post :create, params: {user: user }} + subject { post :create, params: { user: user } } context 'when user is correct' do - it { expect(described_class).to be < Devise::RegistrationsController } - it 'sends welcome email' do expect(WelcomeMailer).to receive(:welcome_email).and_return(WelcomeMailer) expect(WelcomeMailer).to receive(:deliver_now!) @@ -38,7 +36,7 @@ describe Users::RegistrationsController, type: :controller do end context 'when user is not correct' do - let(:user) { {email: '', password: password, password_confirmation: password} } + let(:user) { { email: '', password: password } } it 'not sends welcome email' do expect(WelcomeMailer).not_to receive(:welcome_email) From 8d008a51d096492522700f3a3b344de6a8fa7b8b Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Wed, 21 Jun 2017 16:14:41 +0200 Subject: [PATCH 072/136] [fix #474] display devise errors with flashes --- app/helpers/application_helper.rb | 8 ++++++++ app/helpers/devise_helper.rb | 8 ++++++++ app/views/layouts/_flash_messages.html.haml | 17 ++++++++++------- 3 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 app/helpers/application_helper.rb create mode 100644 app/helpers/devise_helper.rb diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb new file mode 100644 index 000000000..8aed47dc2 --- /dev/null +++ b/app/helpers/application_helper.rb @@ -0,0 +1,8 @@ +module ApplicationHelper + def flash_class(level) + case level + when "notice" then "alert-success" + when "alert" then "alert-danger" + end + end +end diff --git a/app/helpers/devise_helper.rb b/app/helpers/devise_helper.rb new file mode 100644 index 000000000..14afe134f --- /dev/null +++ b/app/helpers/devise_helper.rb @@ -0,0 +1,8 @@ +module DeviseHelper + def devise_error_messages! + if resource.errors.full_messages.any? + flash.now[:alert] = resource.errors.full_messages + end + '' + end +end diff --git a/app/views/layouts/_flash_messages.html.haml b/app/views/layouts/_flash_messages.html.haml index 730c11fde..1c1ec728b 100644 --- a/app/views/layouts/_flash_messages.html.haml +++ b/app/views/layouts/_flash_messages.html.haml @@ -1,8 +1,11 @@ -- if flash.notice.present? || flash.alert.present? +- if flash.any? #flash_message.center - - if flash.notice.present? - .alert.alert-success - = flash.notice - - if flash.alert.present? - .alert.alert-danger - = flash.alert.html_safe + - flash.each do |key, value| + - if value.class == Array + .alert{ class: flash_class(key) } + - value.each do |message| + = message + %br + - else + .alert{ class: flash_class(key) } + = value From 3f90e82d09d121e29a20e9f393c99a1927f174b7 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Wed, 21 Jun 2017 16:16:40 +0200 Subject: [PATCH 073/136] [Fix #327] flashes messages can deal with Arrays --- app/controllers/backoffice/avis_controller.rb | 2 +- app/controllers/backoffice/private_formulaires_controller.rb | 2 +- spec/controllers/backoffice/avis_controller_spec.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/backoffice/avis_controller.rb b/app/controllers/backoffice/avis_controller.rb index 5271626c7..fc773fb40 100644 --- a/app/controllers/backoffice/avis_controller.rb +++ b/app/controllers/backoffice/avis_controller.rb @@ -49,7 +49,7 @@ class Backoffice::AvisController < ApplicationController avis = Avis.find(params[:id]) redirect_to url_for(backoffice_dossier_path(avis.dossier_id)) else - flash[:alert] = gestionnaire.errors.full_messages.join('
') + flash[:alert] = gestionnaire.errors.full_messages redirect_to url_for(avis_sign_up_path(params[:id], email)) end end diff --git a/app/controllers/backoffice/private_formulaires_controller.rb b/app/controllers/backoffice/private_formulaires_controller.rb index 0353b02f3..edd45dbd2 100644 --- a/app/controllers/backoffice/private_formulaires_controller.rb +++ b/app/controllers/backoffice/private_formulaires_controller.rb @@ -11,7 +11,7 @@ class Backoffice::PrivateFormulairesController < ApplicationController if champs_service_errors.empty? flash[:notice] = "Formulaire enregistré" else - flash[:alert] = champs_service_errors.join('
').html_safe + flash[:alert] = champs_service_errors end end diff --git a/spec/controllers/backoffice/avis_controller_spec.rb b/spec/controllers/backoffice/avis_controller_spec.rb index 33d635c97..89239c2ab 100644 --- a/spec/controllers/backoffice/avis_controller_spec.rb +++ b/spec/controllers/backoffice/avis_controller_spec.rb @@ -178,7 +178,7 @@ describe Backoffice::AvisController, type: :controller do it { expect(created_gestionnaire).to be_nil } it { is_expected.to redirect_to avis_sign_up_path(avis_id, invited_email) } - it { expect(flash.alert).to eq('Password : Le mot de passe est vide') } + it { expect(flash.alert).to eq(['Password : Le mot de passe est vide']) } end end end From 3d11a35da8c03696f26f4a1760f892d8244844d5 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Wed, 21 Jun 2017 18:11:44 +0200 Subject: [PATCH 074/136] Remove some commented code --- app/views/dossiers/_show.html.haml | 7 ------ app/views/users/passwords/edit.html.haml | 28 ------------------------ app/views/users/passwords/new.html.haml | 28 ------------------------ 3 files changed, 63 deletions(-) diff --git a/app/views/dossiers/_show.html.haml b/app/views/dossiers/_show.html.haml index a63cafa14..1aa3e24a1 100644 --- a/app/views/dossiers/_show.html.haml +++ b/app/views/dossiers/_show.html.haml @@ -5,10 +5,3 @@ .row.etape.etape-2 = render partial: '/dossiers/etapes/etape2' - - -# - if @facade.procedure.module_api_carto.use_api_carto? - -# .row.etape.etape_3 - -# = render partial: '/dossiers/etapes/etape3' - -# - -# .row.etape.etape_4 - -# = render partial: '/dossiers/etapes/etape4' diff --git a/app/views/users/passwords/edit.html.haml b/app/views/users/passwords/edit.html.haml index d1688e75c..8b7a7a563 100644 --- a/app/views/users/passwords/edit.html.haml +++ b/app/views/users/passwords/edit.html.haml @@ -1,31 +1,3 @@ --#

Sign up

--# --# <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> --# <%= devise_error_messages! %> --# --#
--# <%= f.label :email %>
--# <%= f.email_field :email, autofocus: true %> --#
--# --#
--# <%= f.label :password %> --# <% if @validatable %> --# (<%= @minimum_password_length %> characters minimum) --# <% end %>
--# <%= f.password_field :password, autocomplete: "off" %> --#
--# --#
--# <%= f.label :password_confirmation %>
--# <%= f.password_field :password_confirmation, autocomplete: "off" %> --#
--# --#
--# <%= f.submit "Sign up" %> --#
--# <% end %> - = devise_error_messages! #form-login diff --git a/app/views/users/passwords/new.html.haml b/app/views/users/passwords/new.html.haml index fa5410532..85ad6d44f 100644 --- a/app/views/users/passwords/new.html.haml +++ b/app/views/users/passwords/new.html.haml @@ -1,31 +1,3 @@ --#

Sign up

--# --# <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> --# <%= devise_error_messages! %> --# --#
--# <%= f.label :email %>
--# <%= f.email_field :email, autofocus: true %> --#
--# --#
--# <%= f.label :password %> --# <% if @validatable %> --# (<%= @minimum_password_length %> characters minimum) --# <% end %>
--# <%= f.password_field :password, autocomplete: "off" %> --#
--# --#
--# <%= f.label :password_confirmation %>
--# <%= f.password_field :password_confirmation, autocomplete: "off" %> --#
--# --#
--# <%= f.submit "Sign up" %> --#
--# <% end %> - = devise_error_messages! %br From ba5d816ce6d330b7f1b37464a9dc0ce16d453fd7 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Thu, 22 Jun 2017 11:29:29 +0200 Subject: [PATCH 075/136] [Fix #176] Delete useless new_assignement email --- .../admin/gestionnaires_controller.rb | 2 -- app/mailers/gestionnaire_mailer.rb | 4 ---- .../new_assignement.text.erb | 10 --------- .../admin/gestionnaires_controller_spec.rb | 21 ------------------- 4 files changed, 37 deletions(-) delete mode 100644 app/views/gestionnaire_mailer/new_assignement.text.erb diff --git a/app/controllers/admin/gestionnaires_controller.rb b/app/controllers/admin/gestionnaires_controller.rb index 197489c9c..1964edfe2 100644 --- a/app/controllers/admin/gestionnaires_controller.rb +++ b/app/controllers/admin/gestionnaires_controller.rb @@ -47,7 +47,6 @@ class Admin::GestionnairesController < AdminController User.create(attributes) flash.notice = 'Accompagnateur ajouté' GestionnaireMailer.new_gestionnaire(@gestionnaire.email, @gestionnaire.password).deliver_now! - GestionnaireMailer.new_assignement(@gestionnaire.email, current_administrateur.email).deliver_now! else flash.alert = @gestionnaire.errors.full_messages.join('
').html_safe end @@ -57,7 +56,6 @@ class Admin::GestionnairesController < AdminController if current_administrateur.gestionnaires.include? @gestionnaire flash.alert = 'Accompagnateur déjà ajouté' else - GestionnaireMailer.new_assignement(@gestionnaire.email, current_administrateur.email).deliver_now! @gestionnaire.administrateurs.push current_administrateur flash.notice = 'Accompagnateur ajouté' #TODO Mailer no assign_to diff --git a/app/mailers/gestionnaire_mailer.rb b/app/mailers/gestionnaire_mailer.rb index e5ea4ed91..7ca4fb6ad 100644 --- a/app/mailers/gestionnaire_mailer.rb +++ b/app/mailers/gestionnaire_mailer.rb @@ -3,10 +3,6 @@ class GestionnaireMailer < ApplicationMailer send_mail email, password, "Vous avez été nommé accompagnateur sur la plateforme TPS" end - def new_assignement email, email_admin - send_mail email, email_admin, "Vous avez été assigné à un nouvel administrateur sur la plateforme TPS" - end - def last_week_overview(gestionnaire, overview) headers['X-mailjet-campaign'] = 'last_week_overview' send_mail gestionnaire.email, overview, 'Résumé de la semaine' diff --git a/app/views/gestionnaire_mailer/new_assignement.text.erb b/app/views/gestionnaire_mailer/new_assignement.text.erb deleted file mode 100644 index 9af14a3e6..000000000 --- a/app/views/gestionnaire_mailer/new_assignement.text.erb +++ /dev/null @@ -1,10 +0,0 @@ -Bienvenue sur la plateforme TPS, - -Vous venez d'être assigné à un administrateur sur la plateforme TPS. Voici quelques informations utiles : - - URL : <%= new_gestionnaire_session_url %> - Email administrateur : <%= @args %> - -Bonne journée, - -L'équipe Téléprocédures Simplifiées diff --git a/spec/controllers/admin/gestionnaires_controller_spec.rb b/spec/controllers/admin/gestionnaires_controller_spec.rb index ca1de17d6..ff88341ba 100644 --- a/spec/controllers/admin/gestionnaires_controller_spec.rb +++ b/spec/controllers/admin/gestionnaires_controller_spec.rb @@ -125,27 +125,6 @@ describe Admin::GestionnairesController, type: :controller do expect(GestionnaireMailer).to receive(:deliver_now!) subject end - - it 'Notification email is sent when accompagnateur is assign' do - expect(GestionnaireMailer).to receive(:new_assignement).and_return(GestionnaireMailer) - expect(GestionnaireMailer).to receive(:deliver_now!) - subject - end - - context 'when accompagnateur is assign at a new admin' do - before do - create :gestionnaire, email: email, administrateurs: [admin] - - sign_out admin - sign_in admin_2 - end - - it { - expect(GestionnaireMailer).to receive(:new_assignement).and_return(GestionnaireMailer) - expect(GestionnaireMailer).to receive(:deliver_now!) - subject - } - end end context 'unified login' do From 469f0aed4bb8730bfdb78bc5718c8b93fdd51ab1 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Thu, 22 Jun 2017 12:10:03 +0200 Subject: [PATCH 076/136] [Fix #440] Show the current month only if logged in as superadmin --- app/controllers/stats_controller.rb | 6 ++- spec/controllers/stats_controller_spec.rb | 54 ++++++++++++++++++----- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb index e41dc8c74..a4ac3cbb1 100644 --- a/app/controllers/stats_controller.rb +++ b/app/controllers/stats_controller.rb @@ -38,7 +38,11 @@ class StatsController < ApplicationController def last_four_months_hash(association, date_attribute = :created_at) min_date = 3.months.ago.beginning_of_month.to_date - max_date = Time.now.to_date + if administration_signed_in? + max_date = Time.now.to_date + else + max_date = Time.now.beginning_of_month - 1.second + end association .where(date_attribute => min_date..max_date) diff --git a/spec/controllers/stats_controller_spec.rb b/spec/controllers/stats_controller_spec.rb index 1c28f0d01..09ce7f38c 100644 --- a/spec/controllers/stats_controller_spec.rb +++ b/spec/controllers/stats_controller_spec.rb @@ -5,18 +5,22 @@ describe StatsController, type: :controller do context "without a date attribute" do before do FactoryGirl.create(:procedure, :created_at => 6.months.ago) - FactoryGirl.create(:procedure, :created_at => 45.days.ago) - FactoryGirl.create(:procedure, :created_at => 1.days.ago) - FactoryGirl.create(:procedure, :created_at => 1.days.ago) + FactoryGirl.create(:procedure, :created_at => 62.days.ago) + FactoryGirl.create(:procedure, :created_at => 62.days.ago) + FactoryGirl.create(:procedure, :created_at => 31.days.ago) + + @controller = StatsController.new + + allow(@controller).to receive(:administration_signed_in?).and_return(false) end let (:association) { Procedure.all } - subject { StatsController.new.send(:last_four_months_hash, association) } + subject { @controller.send(:last_four_months_hash, association) } it { expect(subject).to eq([ - [I18n.l(45.days.ago.beginning_of_month, format: "%B %Y"), 1], - [I18n.l(1.days.ago.beginning_of_month, format: "%B %Y"), 2] + [I18n.l(62.days.ago.beginning_of_month, format: "%B %Y"), 2], + [I18n.l(31.days.ago.beginning_of_month, format: "%B %Y"), 1] ]) } end @@ -24,18 +28,44 @@ describe StatsController, type: :controller do context "with a date attribute" do before do FactoryGirl.create(:procedure, :created_at => 6.months.ago, :updated_at => 6.months.ago) - FactoryGirl.create(:procedure, :created_at => 2.months.ago, :updated_at => 45.days.ago) - FactoryGirl.create(:procedure, :created_at => 2.months.ago, :updated_at => 45.days.ago) - FactoryGirl.create(:procedure, :created_at => 2.months.ago, :updated_at => 1.days.ago) + FactoryGirl.create(:procedure, :created_at => 2.months.ago, :updated_at => 62.days.ago) + FactoryGirl.create(:procedure, :created_at => 2.months.ago, :updated_at => 62.days.ago) + FactoryGirl.create(:procedure, :created_at => 2.months.ago, :updated_at => 31.days.ago) + @controller = StatsController.new + + allow(@controller).to receive(:administration_signed_in?).and_return(false) end let (:association) { Procedure.all } - subject { StatsController.new.send(:last_four_months_hash, association, :updated_at) } + subject { @controller.send(:last_four_months_hash, association, :updated_at) } it { expect(subject).to eq([ - [I18n.l(45.days.ago.beginning_of_month, format: "%B %Y"), 2], - [I18n.l(1.days.ago.beginning_of_month, format: "%B %Y"), 1] + [I18n.l(62.days.ago.beginning_of_month, format: "%B %Y"), 2], + [I18n.l(31.days.ago.beginning_of_month, format: "%B %Y"), 1] + ]) + } + end + + context "while a super admin is logged in" do + before do + FactoryGirl.create(:procedure, :created_at => 6.months.ago) + FactoryGirl.create(:procedure, :created_at => 45.days.ago) + FactoryGirl.create(:procedure, :created_at => 1.day.ago) + FactoryGirl.create(:procedure, :created_at => 1.day.ago) + + @controller = StatsController.new + + allow(@controller).to receive(:administration_signed_in?).and_return(true) + end + + let (:association) { Procedure.all } + + subject { @controller.send(:last_four_months_hash, association) } + + it { expect(subject).to eq([ + [I18n.l(45.days.ago.beginning_of_month, format: "%B %Y"), 1], + [I18n.l(1.days.ago.beginning_of_month, format: "%B %Y"), 2] ]) } end From bd2fcebf95a87efad09a67381e3ed5bee9becef3 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Thu, 22 Jun 2017 16:28:38 +0200 Subject: [PATCH 077/136] Change a silly title --- app/views/admin/procedures/_modal_transfer.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/procedures/_modal_transfer.html.haml b/app/views/admin/procedures/_modal_transfer.html.haml index 733b19936..cf9c9847b 100644 --- a/app/views/admin/procedures/_modal_transfer.html.haml +++ b/app/views/admin/procedures/_modal_transfer.html.haml @@ -6,7 +6,7 @@ %button.close{ "aria-label" => "Close", "data-dismiss" => "modal", :type => "button" } %span{ "aria-hidden" => "true" } × %h4#myModalLabel.modal-title - Petit transfert de procédure entre administrateur + Transférer la procédure à un autre administrateur .modal-body %p Cette fonctionnalité vous permet de transmettre un clone de votre procédure à un autre administrateur. From 0e3f431d7db19f32614ba854748ae67e91897545 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Thu, 22 Jun 2017 16:34:34 +0200 Subject: [PATCH 078/136] Fix indentation in _modal_transfer.html.haml --- app/views/admin/procedures/_modal_transfer.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/admin/procedures/_modal_transfer.html.haml b/app/views/admin/procedures/_modal_transfer.html.haml index cf9c9847b..512e4c75a 100644 --- a/app/views/admin/procedures/_modal_transfer.html.haml +++ b/app/views/admin/procedures/_modal_transfer.html.haml @@ -12,9 +12,9 @@ Cette fonctionnalité vous permet de transmettre un clone de votre procédure à un autre administrateur. %div{ style:'margin-top:20px' } = text_field_tag :email_admin, '', { class: 'form-control', - type: 'email', - placeholder: 'Email administrateur cible', - style: 'width: 300px; margin-left:auto; margin-right:auto' } + type: 'email', + placeholder: 'Email administrateur cible', + style: 'width: 300px; margin-left:auto; margin-right:auto' } #not_found_admin.center.text-danger{ style:'display: none; margin-top: 10px;' } Cet administrateur n'existe pas. .modal-footer From 8d9ca18406cb210467ed7d41626b8924c2b15365 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Thu, 22 Jun 2017 16:35:05 +0200 Subject: [PATCH 079/136] Improve spacing in _model_transfer.html.haml --- app/views/admin/procedures/_modal_transfer.html.haml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/views/admin/procedures/_modal_transfer.html.haml b/app/views/admin/procedures/_modal_transfer.html.haml index 512e4c75a..66edd79ab 100644 --- a/app/views/admin/procedures/_modal_transfer.html.haml +++ b/app/views/admin/procedures/_modal_transfer.html.haml @@ -5,18 +5,23 @@ .modal-header %button.close{ "aria-label" => "Close", "data-dismiss" => "modal", :type => "button" } %span{ "aria-hidden" => "true" } × + %h4#myModalLabel.modal-title Transférer la procédure à un autre administrateur + .modal-body %p Cette fonctionnalité vous permet de transmettre un clone de votre procédure à un autre administrateur. + %div{ style:'margin-top:20px' } = text_field_tag :email_admin, '', { class: 'form-control', type: 'email', placeholder: 'Email administrateur cible', style: 'width: 300px; margin-left:auto; margin-right:auto' } + #not_found_admin.center.text-danger{ style:'display: none; margin-top: 10px;' } Cet administrateur n'existe pas. + .modal-footer = submit_tag "Envoyer", class: 'btn btn-success' = button_tag 'Annuler', class: %w(btn btn btn-default), id: 'cancel', data: { dismiss: 'modal' } From a3e3284c5d81528b2cf3593dacb3bc4de7d788ce Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Thu, 22 Jun 2017 16:36:11 +0200 Subject: [PATCH 080/136] Fix a spelling mistake --- app/controllers/admin/procedures_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/procedures_controller.rb b/app/controllers/admin/procedures_controller.rb index 2b7cb2adf..f2d950083 100644 --- a/app/controllers/admin/procedures_controller.rb +++ b/app/controllers/admin/procedures_controller.rb @@ -131,7 +131,7 @@ class Admin::ProceduresController < AdminController clone_procedure.administrateur = admin clone_procedure.save - flash.now.notice = "La procédure a correctement été cloné vers le nouvel administrateur." + flash.now.notice = "La procédure a correctement été clonée vers le nouvel administrateur." render '/admin/procedures/transfer', formats: 'js', status: 200 end From 0ea29d131fa131c92582b7ff0bed8d3d217f4a90 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Thu, 22 Jun 2017 16:45:52 +0200 Subject: [PATCH 081/136] Refactor Admin::ProceduresController#transfer --- app/controllers/admin/procedures_controller.rb | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/controllers/admin/procedures_controller.rb b/app/controllers/admin/procedures_controller.rb index f2d950083..434221619 100644 --- a/app/controllers/admin/procedures_controller.rb +++ b/app/controllers/admin/procedures_controller.rb @@ -123,17 +123,19 @@ class Admin::ProceduresController < AdminController def transfer admin = Administrateur.find_by_email(params[:email_admin]) - return render '/admin/procedures/transfer', formats: 'js', status: 404 if admin.nil? + if admin.nil? + render '/admin/procedures/transfer', formats: 'js', status: 404 + else + procedure = current_administrateur.procedures.find(params[:procedure_id]) + clone_procedure = procedure.clone - procedure = current_administrateur.procedures.find(params[:procedure_id]) - clone_procedure = procedure.clone + clone_procedure.administrateur = admin + clone_procedure.save - clone_procedure.administrateur = admin - clone_procedure.save + flash.now.notice = "La procédure a correctement été clonée vers le nouvel administrateur." - flash.now.notice = "La procédure a correctement été clonée vers le nouvel administrateur." - - render '/admin/procedures/transfer', formats: 'js', status: 200 + render '/admin/procedures/transfer', formats: 'js', status: 200 + end end def archive From 12f61b490ae419dbe0a09d4601c39f880c0743b5 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Thu, 22 Jun 2017 16:46:01 +0200 Subject: [PATCH 082/136] Reformat transfer.js.erb --- app/views/admin/procedures/transfer.js.erb | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/app/views/admin/procedures/transfer.js.erb b/app/views/admin/procedures/transfer.js.erb index d57f2f48f..2df194e24 100644 --- a/app/views/admin/procedures/transfer.js.erb +++ b/app/views/admin/procedures/transfer.js.erb @@ -1,11 +1,14 @@ <%- if response.status == 404 %> transfer_errors_message(true); <%- else %> - <% flash.each do |type, message| %> + <% flash.each do |type, message| %> $("#flash_message").html("
<%= message.html_safe %>
") - <% end %> - <% flash.clear %> - transfer_errors_message(false); - $("#email_admin").val(''); - $("button#cancel").click(); -<%- end %> \ No newline at end of file + <% end %> + + <% flash.clear %> + + transfer_errors_message(false); + + $("#email_admin").val(''); + $("button#cancel").click(); +<%- end %> From ed51c77895c8ba6cb1b6c8753ab93e6e357514c6 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Thu, 22 Jun 2017 17:53:30 +0200 Subject: [PATCH 083/136] Correctly display the success message when transfer was successful --- app/views/admin/procedures/transfer.js.erb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/views/admin/procedures/transfer.js.erb b/app/views/admin/procedures/transfer.js.erb index 2df194e24..26384d4a9 100644 --- a/app/views/admin/procedures/transfer.js.erb +++ b/app/views/admin/procedures/transfer.js.erb @@ -1,14 +1,12 @@ <%- if response.status == 404 %> transfer_errors_message(true); <%- else %> - <% flash.each do |type, message| %> - $("#flash_message").html("
<%= message.html_safe %>
") - <% end %> - + $("#main-container").prepend("
"); + $("#flash_message").prepend("
<%= flash.notice.html_safe %>
"); <% flash.clear %> transfer_errors_message(false); - $("#email_admin").val(''); + $("button#cancel").click(); <%- end %> From 96dd853dbb68ab0f427433edb31bbb88abd2ce45 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Thu, 22 Jun 2017 17:59:15 +0200 Subject: [PATCH 084/136] =?UTF-8?q?[Fix=20#396]=20Don=E2=80=99t=20submit?= =?UTF-8?q?=20the=20publish=20form=20after=20a=20transfer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/views/admin/procedures/transfer.js.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/procedures/transfer.js.erb b/app/views/admin/procedures/transfer.js.erb index 26384d4a9..02e1bbdf6 100644 --- a/app/views/admin/procedures/transfer.js.erb +++ b/app/views/admin/procedures/transfer.js.erb @@ -8,5 +8,5 @@ transfer_errors_message(false); $("#email_admin").val(''); - $("button#cancel").click(); + $("#transferModal button#cancel").click(); <%- end %> From 0e3ebfd819d271c477116e83b1028b40dc9db711 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 26 Jun 2017 11:00:36 +0200 Subject: [PATCH 085/136] =?UTF-8?q?[Fix=20#183]=20Display=20=E2=80=9Coui?= =?UTF-8?q?=E2=80=9D=20or=20=E2=80=9Cnon=E2=80=9D=20instead=20of=20?= =?UTF-8?q?=E2=80=9Ctrue=E2=80=9D=20or=20=E2=80=9Cfalse=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/views/dossiers/_infos_dossier.html.haml | 5 ++ spec/factories/procedure.rb | 8 +++ spec/factories/type_de_champ_public.rb | 5 ++ spec/views/dossiers/_infos_dossier_spec.rb | 72 +++++++++++++++++---- 4 files changed, 77 insertions(+), 13 deletions(-) diff --git a/app/views/dossiers/_infos_dossier.html.haml b/app/views/dossiers/_infos_dossier.html.haml index 7de372ba2..f607632e6 100644 --- a/app/views/dossiers/_infos_dossier.html.haml +++ b/app/views/dossiers/_infos_dossier.html.haml @@ -46,6 +46,11 @@ = dossier.text_summary - else Pas de dossier associé + - elsif champ.type_champ == 'yes_no' + - if champ.decorate.value == 'true' + Oui + - elsif champ.decorate.value == 'false' + Non - else = champ.decorate.value.html_safe diff --git a/spec/factories/procedure.rb b/spec/factories/procedure.rb index 9e0b76e7b..0f240362c 100644 --- a/spec/factories/procedure.rb +++ b/spec/factories/procedure.rb @@ -75,6 +75,14 @@ FactoryGirl.define do end end + trait :with_yes_no do + after(:build) do |procedure, _evaluator| + type_de_champ = create(:type_de_champ_public, :type_yes_no) + + procedure.types_de_champ << type_de_champ + end + end + trait :with_two_type_de_piece_justificative do after(:build) do |procedure, _evaluator| rib = create(:type_de_piece_justificative, :rib, order_place: 1) diff --git a/spec/factories/type_de_champ_public.rb b/spec/factories/type_de_champ_public.rb index b1ef85016..e11b90e33 100644 --- a/spec/factories/type_de_champ_public.rb +++ b/spec/factories/type_de_champ_public.rb @@ -14,5 +14,10 @@ FactoryGirl.define do libelle 'Référence autre dossier' type_champ 'dossier_link' end + + trait :type_yes_no do + libelle 'Yes/no' + type_champ 'yes_no' + end end end diff --git a/spec/views/dossiers/_infos_dossier_spec.rb b/spec/views/dossiers/_infos_dossier_spec.rb index ff2448229..ff7af8d36 100644 --- a/spec/views/dossiers/_infos_dossier_spec.rb +++ b/spec/views/dossiers/_infos_dossier_spec.rb @@ -1,25 +1,71 @@ require 'spec_helper' describe 'dossiers/_infos_dossier.html.haml', type: :view do - let(:dossier) { create(:dossier, :with_entreprise, procedure: create(:procedure, :with_api_carto, :with_type_de_champ)) } + describe "champs rendering" do + let(:dossier) { create(:dossier, :with_entreprise, procedure: create(:procedure, :with_api_carto, :with_type_de_champ)) } - before do - champs.each do |champ| - champ.value = ((0...8).map { (65 + rand(26)).chr }.join) - champ.save + before do + champs.each do |champ| + champ.value = ((0...8).map { (65 + rand(26)).chr }.join) + champ.save + end + + assign(:facade, DossierFacades.new(dossier.id, dossier.user.email)) + render end - assign(:facade, DossierFacades.new(dossier.id, dossier.user.email)) - render + describe 'every champs are present on the page' do + let(:champs) { dossier.champs } + + it { expect(rendered).to have_content(champs.first.libelle) } + it { expect(rendered).to have_content(champs.first.value) } + + it { expect(rendered).to have_content(champs.last.libelle) } + it { expect(rendered).to have_content(champs.last.value) } + end end - describe 'every champs are present on the page' do - let(:champs) { dossier.champs } + describe "oui_non champ rendering" do + let(:dossier_with_yes_no) { create(:dossier, procedure: create(:procedure, :with_yes_no)) } - it { expect(rendered).to have_content(champs.first.libelle) } - it { expect(rendered).to have_content(champs.first.value) } + context "with the true value" do + before do + oui_non_champ = dossier_with_yes_no.champs.first + oui_non_champ.value = 'true' + oui_non_champ.save - it { expect(rendered).to have_content(champs.last.libelle) } - it { expect(rendered).to have_content(champs.last.value) } + assign(:facade, DossierFacades.new(dossier_with_yes_no.id, dossier_with_yes_no.user.email)) + render + end + + it { expect(rendered).to have_content("Oui") } + end + + context "with the false value" do + before do + oui_non_champ = dossier_with_yes_no.champs.first + oui_non_champ.value = 'false' + oui_non_champ.save + + assign(:facade, DossierFacades.new(dossier_with_yes_no.id, dossier_with_yes_no.user.email)) + render + end + + it { expect(rendered).to have_content("Non") } + end + + context "with no value" do + before do + oui_non_champ = dossier_with_yes_no.champs.first + oui_non_champ.value = nil + oui_non_champ.save + + assign(:facade, DossierFacades.new(dossier_with_yes_no.id, dossier_with_yes_no.user.email)) + render + end + + it { expect(rendered).not_to have_content("Oui") } + it { expect(rendered).not_to have_content("Non") } + end end end From d69d0f8a78aaaa92d72c38c765384ae3f8418969 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Thu, 22 Jun 2017 14:58:29 +0200 Subject: [PATCH 086/136] Etablissement: add inline_adresse --- app/models/etablissement.rb | 5 +++++ spec/models/etablissement_spec.rb | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/app/models/etablissement.rb b/app/models/etablissement.rb index 44e840205..5e480ae85 100644 --- a/app/models/etablissement.rb +++ b/app/models/etablissement.rb @@ -9,4 +9,9 @@ class Etablissement < ActiveRecord::Base def geo_adresse [numero_voie, type_voie, nom_voie, complement_adresse, code_postal, localite].join(' ') end + + def inline_adresse + #squeeze needed because of space in excess in the data + "#{numero_voie} #{type_voie} #{nom_voie}, #{complement_adresse}, #{code_postal} #{localite}".squeeze(' ') + end end diff --git a/spec/models/etablissement_spec.rb b/spec/models/etablissement_spec.rb index 802672340..c89d5ec43 100644 --- a/spec/models/etablissement_spec.rb +++ b/spec/models/etablissement_spec.rb @@ -29,4 +29,10 @@ describe Etablissement do it { is_expected.to eq '6 RUE RAOUL NORDLING IMMEUBLE BORA 92270 BOIS COLOMBES' } end + + describe '#inline_adresse' do + let(:etablissement) { create(:etablissement, nom_voie: 'green moon') } + + it { expect(etablissement.inline_adresse).to eq '6 RUE green moon, IMMEUBLE BORA, 92270 BOIS COLOMBES' } + end end From 84e3d8fbe0391bc192a8109ff544f00eefc782e4 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Thu, 22 Jun 2017 15:00:25 +0200 Subject: [PATCH 087/136] Attestation_template: use etablissement inline_adresse --- app/models/attestation_template.rb | 2 +- spec/models/attestation_template_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/attestation_template.rb b/app/models/attestation_template.rb index 4b36d53ef..9bac00521 100644 --- a/app/models/attestation_template.rb +++ b/app/models/attestation_template.rb @@ -75,7 +75,7 @@ class AttestationTemplate < ApplicationRecord end def etablissement_tags - [{ libelle: 'adresse', description: '', target: 'adresse' }] + [{ libelle: 'adresse', description: '', target: 'inline_adresse' }] end def build_pdf(dossier) diff --git a/spec/models/attestation_template_spec.rb b/spec/models/attestation_template_spec.rb index 873d2a112..a9d5e5191 100644 --- a/spec/models/attestation_template_spec.rb +++ b/spec/models/attestation_template_spec.rb @@ -159,7 +159,7 @@ describe AttestationTemplate, type: :model do let(:etablissement) { create(:etablissement, adresse: 'adresse') } let(:template_title) { '--adresse--' } - it { expect(view_args[:title]).to eq('adresse') } + it { expect(view_args[:title]).to eq(etablissement.inline_adresse) } end end end From c16c19f8e6a71917cadea5088bd3a44027c8f867 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Tue, 27 Jun 2017 11:40:55 +0200 Subject: [PATCH 088/136] [Fix #499] Attestation: footer is now an text_field instead of a text_area --- app/views/admin/attestation_templates/edit.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/attestation_templates/edit.html.haml b/app/views/admin/attestation_templates/edit.html.haml index 5c50f5023..31b5b4726 100644 --- a/app/views/admin/attestation_templates/edit.html.haml +++ b/app/views/admin/attestation_templates/edit.html.haml @@ -58,7 +58,7 @@ .form-group = f.label :footer, 'Adresse en bas de page' - ~ f.text_area :footer, class: 'form-control', rows: 2, + = f.text_field :footer, class: 'form-control', placeholder: "Direction interministérielle du numérique et du système d'information et de communication de l'Etat (DINSIC)" %button.btn.btn-primary{ formaction: admin_procedure_attestation_template_preview_path, formtarget: '_blank' } Prévisualiser From 81984d28836bc1275f4dc1cbd75c41a599d99e48 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Tue, 27 Jun 2017 11:41:35 +0200 Subject: [PATCH 089/136] Attestation: remove misleading footer placeholder --- app/views/admin/attestation_templates/edit.html.haml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/views/admin/attestation_templates/edit.html.haml b/app/views/admin/attestation_templates/edit.html.haml index 31b5b4726..43bbab1fa 100644 --- a/app/views/admin/attestation_templates/edit.html.haml +++ b/app/views/admin/attestation_templates/edit.html.haml @@ -58,8 +58,7 @@ .form-group = f.label :footer, 'Adresse en bas de page' - = f.text_field :footer, class: 'form-control', - placeholder: "Direction interministérielle du numérique et du système d'information et de communication de l'Etat (DINSIC)" + = f.text_field :footer, class: 'form-control' %button.btn.btn-primary{ formaction: admin_procedure_attestation_template_preview_path, formtarget: '_blank' } Prévisualiser From 3d9940749581e5566494bb73c658463055f2cbc6 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Tue, 27 Jun 2017 13:07:06 +0200 Subject: [PATCH 090/136] [Fix #499] Attestation: add maxlength to footer --- app/models/attestation_template.rb | 1 + .../admin/attestation_templates/edit.html.haml | 2 +- config/locales/fr.yml | 4 ++++ spec/models/attestation_template_spec.rb | 15 +++++++++++++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/models/attestation_template.rb b/app/models/attestation_template.rb index 9bac00521..9c2b5ad49 100644 --- a/app/models/attestation_template.rb +++ b/app/models/attestation_template.rb @@ -7,6 +7,7 @@ class AttestationTemplate < ApplicationRecord mount_uploader :signature, AttestationTemplateImageUploader validate :logo_signature_file_size + validates :footer, length: { maximum: 190 } FILE_MAX_SIZE_IN_MB = 0.5 diff --git a/app/views/admin/attestation_templates/edit.html.haml b/app/views/admin/attestation_templates/edit.html.haml index 43bbab1fa..9b54d4def 100644 --- a/app/views/admin/attestation_templates/edit.html.haml +++ b/app/views/admin/attestation_templates/edit.html.haml @@ -58,7 +58,7 @@ .form-group = f.label :footer, 'Adresse en bas de page' - = f.text_field :footer, class: 'form-control' + = f.text_field :footer, class: 'form-control', maxlength: 190 %button.btn.btn-primary{ formaction: admin_procedure_attestation_template_preview_path, formtarget: '_blank' } Prévisualiser diff --git a/config/locales/fr.yml b/config/locales/fr.yml index f1da0acaa..b94806305 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -87,6 +87,10 @@ fr: activerecord: errors: models: + attestation_template: + attributes: + footer: + too_long: ": l'adresse en bas de page est trop longue." piece_justificative: attributes: content: diff --git a/spec/models/attestation_template_spec.rb b/spec/models/attestation_template_spec.rb index a9d5e5191..95270dbb9 100644 --- a/spec/models/attestation_template_spec.rb +++ b/spec/models/attestation_template_spec.rb @@ -40,6 +40,21 @@ describe AttestationTemplate, type: :model do end end + describe 'validates footer length' do + let(:attestation_template) { AttestationTemplate.new(footer: footer) } + + subject do + attestation_template.validate + attestation_template.errors.details + end + + context 'when the footer is too long' do + let(:footer) { 'a' * 191 } + + it { is_expected.to match({ footer: [{ error: :too_long, count: 190 }] }) } + end + end + describe 'dup' do before do @logo = File.open('spec/fixtures/white.png') From 68b9cd209d09d47ff69e3a36342ea53135ff0404 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 27 Jun 2017 13:38:54 +0200 Subject: [PATCH 091/136] Remove tests that were just testing if Rails worked --- spec/models/procedure_spec.rb | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index d0f36ecea..0c88be211 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -219,22 +219,6 @@ describe Procedure do end end - describe 'publish' do - let(:procedure) { create(:procedure, :published) } - let(:procedure_path) { ProcedurePath.find(procedure.procedure_path.id) } - - it 'is available from a valid path' do - expect(procedure.path).to match(/fake_path/) - expect(procedure.published).to be_truthy - end - - it 'is correctly set in ProcedurePath table' do - expect(ProcedurePath.where(path: procedure.path).count).to eq(1) - expect(procedure_path.procedure_id).to eq(procedure.id) - expect(procedure_path.administrateur_id).to eq(procedure.administrateur_id) - end - end - describe 'archive' do let(:procedure) { create(:procedure, :published) } let(:procedure_path) { ProcedurePath.find(procedure.procedure_path.id) } From 99ee68451abc3d601af787f5c05484043673556e Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 27 Jun 2017 13:48:27 +0200 Subject: [PATCH 092/136] Add proper tests for Procedure#publish! --- spec/models/procedure_spec.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 0c88be211..7f37f0833 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -219,6 +219,20 @@ describe Procedure do end end + describe '#publish!' do + let(:procedure) { create(:procedure) } + + before do + procedure.publish!("example-path") + end + + it { expect(procedure.published).to eq(true) } + it { expect(procedure.archived).to eq(false) } + it { expect(ProcedurePath.find_by_path("example-path")).to be } + it { expect(ProcedurePath.find_by_path("example-path").procedure).to eq(procedure) } + it { expect(ProcedurePath.find_by_path("example-path").administrateur).to eq(procedure.administrateur) } + end + describe 'archive' do let(:procedure) { create(:procedure, :published) } let(:procedure_path) { ProcedurePath.find(procedure.procedure_path.id) } From 1b808d9abe648a6e845433abb0a6a1208b4b5587 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 27 Jun 2017 11:22:44 +0200 Subject: [PATCH 093/136] Add the AddPublishedAtToProcedure migration --- db/migrate/20170627091953_add_published_at_to_procedure.rb | 5 +++++ db/schema.rb | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20170627091953_add_published_at_to_procedure.rb diff --git a/db/migrate/20170627091953_add_published_at_to_procedure.rb b/db/migrate/20170627091953_add_published_at_to_procedure.rb new file mode 100644 index 000000000..8f46123e5 --- /dev/null +++ b/db/migrate/20170627091953_add_published_at_to_procedure.rb @@ -0,0 +1,5 @@ +class AddPublishedAtToProcedure < ActiveRecord::Migration[5.0] + def change + add_column :procedures, :published_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index 842509ac5..cd1760bc5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170601123221) do +ActiveRecord::Schema.define(version: 20170627091953) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -381,7 +381,8 @@ ActiveRecord::Schema.define(version: 20170601123221) do t.string "lien_notice" t.boolean "for_individual", default: false t.boolean "individual_with_siret", default: false - t.date "auto_archive_on" + t.datetime "auto_archive_on" + t.datetime "published_at" end create_table "quartier_prioritaires", force: :cascade do |t| @@ -500,4 +501,5 @@ ActiveRecord::Schema.define(version: 20170601123221) do LEFT JOIN individuals ON ((individuals.dossier_id = dossiers.id))) LEFT JOIN pieces_justificatives ON ((pieces_justificatives.dossier_id = dossiers.id))); SQL + end From cf5398fe99b3a1514eda6509fad25361b7aab837 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 27 Jun 2017 11:23:02 +0200 Subject: [PATCH 094/136] Record published_at when publishing a procedure --- app/models/procedure.rb | 2 +- spec/models/procedure_spec.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 38c7200e3..c5a0ce236 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -112,7 +112,7 @@ class Procedure < ActiveRecord::Base end def publish!(path) - self.update_attributes!({published: true, archived: false}) + self.update_attributes!({ published: true, archived: false, published_at: Time.now }) ProcedurePath.create!(path: path, procedure: self, administrateur: self.administrateur) end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 7f37f0833..88e3fbd16 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -224,10 +224,12 @@ describe Procedure do before do procedure.publish!("example-path") + Timecop.freeze(Time.now) end it { expect(procedure.published).to eq(true) } it { expect(procedure.archived).to eq(false) } + it { expect(procedure.published_at).to eq(Time.now) } it { expect(ProcedurePath.find_by_path("example-path")).to be } it { expect(ProcedurePath.find_by_path("example-path").procedure).to eq(procedure) } it { expect(ProcedurePath.find_by_path("example-path").administrateur).to eq(procedure.administrateur) } From bb57002eff270deb38bf29d89c31a9bb002bb391 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 26 Jun 2017 16:06:53 +0200 Subject: [PATCH 095/136] [Fix #352] Downcase admin emails when transferring a procedure See config/initializers/devise.rb:L44 config.case_insensitive_keys = [ :email ] We therefore need to downcase the email before looking for an admin by its email address --- .../admin/procedures_controller.rb | 2 +- .../admin/procedures_controller_spec.rb | 26 ++++++++++++++----- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/app/controllers/admin/procedures_controller.rb b/app/controllers/admin/procedures_controller.rb index 434221619..df49606fe 100644 --- a/app/controllers/admin/procedures_controller.rb +++ b/app/controllers/admin/procedures_controller.rb @@ -121,7 +121,7 @@ class Admin::ProceduresController < AdminController end def transfer - admin = Administrateur.find_by_email(params[:email_admin]) + admin = Administrateur.find_by_email(params[:email_admin].downcase) if admin.nil? render '/admin/procedures/transfer', formats: 'js', status: 404 diff --git a/spec/controllers/admin/procedures_controller_spec.rb b/spec/controllers/admin/procedures_controller_spec.rb index db036feed..d0705b7cd 100644 --- a/spec/controllers/admin/procedures_controller_spec.rb +++ b/spec/controllers/admin/procedures_controller_spec.rb @@ -500,20 +500,32 @@ describe Admin::ProceduresController, type: :controller do it { expect(subject.status).to eq 404 } end - context 'when admin is know' do - let(:new_admin) { create :administrateur, email: 'new_admin@admin.com' } - let(:email_admin) { new_admin.email } + context 'when admin is known' do + let!(:new_admin) { create :administrateur, email: 'new_admin@admin.com' } - it { expect(subject.status).to eq 200 } - it { expect { subject }.to change(Procedure, :count).by(1) } + context "and its email address is correct" do + let(:email_admin) { 'new_admin@admin.com' } + + it { expect(subject.status).to eq 200 } + it { expect { subject }.to change(Procedure, :count).by(1) } + end + + context 'when admin is know but its email was not downcased' do + let(:email_admin) { "NEW_admin@adMIN.com" } + + it { expect(subject.status).to eq 200 } + it { expect { subject }.to change(Procedure, :count).by(1) } + end + + describe "correctly assigns the new admin" do + let(:email_admin) { 'new_admin@admin.com' } - context { before do subject end it { expect(Procedure.last.administrateur).to eq new_admin } - } + end end end end From 556359cd058149f6f3b631d60c4ae80475d79ded Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 27 Jun 2017 10:50:29 +0200 Subject: [PATCH 096/136] =?UTF-8?q?[Fix=20#318]=20Add=20annotations=20priv?= =?UTF-8?q?=C3=A9es=20to=20exported=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/dossier.rb | 2 ++ spec/models/dossier_spec.rb | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 5eb69e305..fd5d23838 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -242,6 +242,7 @@ class Dossier < ActiveRecord::Base serialized_dossier = DossierTableExportSerializer.new(self) data = serialized_dossier.attributes.values data += self.champs.order('type_de_champ_id ASC').map(&:value) + data += self.champs_private.order('type_de_champ_id ASC').map(&:value) data += self.export_entreprise_data.values return data end @@ -250,6 +251,7 @@ class Dossier < ActiveRecord::Base serialized_dossier = DossierTableExportSerializer.new(self) headers = serialized_dossier.attributes.keys headers += self.procedure.types_de_champ.order('id ASC').map { |types_de_champ| types_de_champ.libelle.parameterize.underscore.to_sym } + headers += self.procedure.types_de_champ_private.order('id ASC').map { |types_de_champ| types_de_champ.libelle.parameterize.underscore.to_sym } headers += self.export_entreprise_data.keys return headers end diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index 958afca70..21d4c6c4c 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -486,7 +486,7 @@ describe Dossier do end context 'when dossier is followed' do - let(:procedure) { create(:procedure, :with_type_de_champ) } + let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private) } let(:gestionnaire) { create(:gestionnaire) } let(:follow) { create(:follow, gestionnaire: gestionnaire) } let(:date1) { 1.day.ago } @@ -502,7 +502,11 @@ describe Dossier do it { expect(subject).to include(:individual_nom) } it { expect(subject).to include(:individual_prenom) } it { expect(subject).to include(:individual_birthdate) } - it { expect(subject.count).to eq(DossierTableExportSerializer.new(dossier).attributes.count + dossier.procedure.types_de_champ.count + dossier.export_entreprise_data.count) } + it { expect(subject.count).to eq(DossierTableExportSerializer.new(dossier).attributes.count + + dossier.procedure.types_de_champ.count + + dossier.procedure.types_de_champ_private.count + + dossier.export_entreprise_data.count) + } end describe '#data_with_champs' do @@ -523,7 +527,12 @@ describe Dossier do it { expect(subject[12]).to be_nil } it { expect(subject[13]).to be_nil } it { expect(subject[14]).to be_nil } - it { expect(subject.count).to eq(DossierTableExportSerializer.new(dossier).attributes.count + dossier.procedure.types_de_champ.count + dossier.export_entreprise_data.count) } + it { expect(subject[15]).to be_nil } + it { expect(subject.count).to eq(DossierTableExportSerializer.new(dossier).attributes.count + + dossier.procedure.types_de_champ.count + + dossier.procedure.types_de_champ_private.count + + dossier.export_entreprise_data.count) + } context 'dossier for individual' do let(:dossier_with_individual) { create(:dossier, :for_individual, user: user, procedure: procedure) } @@ -556,6 +565,7 @@ describe Dossier do nil, nil, nil, + nil, "44011762001530", "true", "4950Z", From aeeb0dd22f4f25006f9d286fbfacda1c9878236f Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 26 Jun 2017 17:18:47 +0200 Subject: [PATCH 097/136] =?UTF-8?q?[Fix=20#170]=20Keep=20drop=20down=20men?= =?UTF-8?q?u=20values=20for=20annot.=20priv=C3=A9es=20when=20cloning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/procedure.rb | 15 +++++++-------- spec/factories/drop_down_list.rb | 2 +- spec/factories/type_de_champ_private.rb | 6 ++++++ spec/factories/type_de_champ_public.rb | 6 ++++++ spec/models/procedure_spec.rb | 4 ++++ 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/app/models/procedure.rb b/app/models/procedure.rb index c5a0ce236..8db90cdef 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -89,14 +89,13 @@ class Procedure < ActiveRecord::Base def clone procedure = self.deep_clone(include: - [ - :types_de_piece_justificative, - :types_de_champ, - :types_de_champ_private, - :module_api_carto, - :attestation_template, - types_de_champ: [:drop_down_list] - ]) + { + types_de_piece_justificative: nil, + module_api_carto: nil, + attestation_template: nil, + types_de_champ: :drop_down_list, + types_de_champ_private: :drop_down_list + }) procedure.archived = false procedure.published = false procedure.logo_secure_token = nil diff --git a/spec/factories/drop_down_list.rb b/spec/factories/drop_down_list.rb index 6b81511cf..0aeabf30d 100644 --- a/spec/factories/drop_down_list.rb +++ b/spec/factories/drop_down_list.rb @@ -1,5 +1,5 @@ FactoryGirl.define do factory :drop_down_list do - value '' + value "val1\r\nval2\r\n--separateur--\r\nval3" end end diff --git a/spec/factories/type_de_champ_private.rb b/spec/factories/type_de_champ_private.rb index 408279d05..356028d91 100644 --- a/spec/factories/type_de_champ_private.rb +++ b/spec/factories/type_de_champ_private.rb @@ -5,5 +5,11 @@ FactoryGirl.define do type_champ 'text' order_place 1 mandatory false + + trait :type_drop_down_list do + libelle 'Menu déroulant' + type_champ 'drop_down_list' + drop_down_list { create(:drop_down_list) } + end end end diff --git a/spec/factories/type_de_champ_public.rb b/spec/factories/type_de_champ_public.rb index e11b90e33..307ec2077 100644 --- a/spec/factories/type_de_champ_public.rb +++ b/spec/factories/type_de_champ_public.rb @@ -19,5 +19,11 @@ FactoryGirl.define do libelle 'Yes/no' type_champ 'yes_no' end + + trait :type_drop_down_list do + libelle 'Menu déroulant' + type_champ 'drop_down_list' + drop_down_list { create(:drop_down_list) } + end end end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 88e3fbd16..69f15588a 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -147,8 +147,10 @@ describe Procedure do let(:procedure) { create(:procedure, archived: archived, published: published, received_mail: received_mail) } let!(:type_de_champ_0) { create(:type_de_champ_public, procedure: procedure, order_place: 0) } let!(:type_de_champ_1) { create(:type_de_champ_public, procedure: procedure, order_place: 1) } + let!(:type_de_champ_2) { create(:type_de_champ_public, :type_drop_down_list, procedure: procedure, order_place: 2) } let!(:type_de_champ_private_0) { create(:type_de_champ_private, procedure: procedure, order_place: 0) } let!(:type_de_champ_private_1) { create(:type_de_champ_private, procedure: procedure, order_place: 1) } + let!(:type_de_champ_private_2) { create(:type_de_champ_private, :type_drop_down_list, procedure: procedure, order_place: 2) } let!(:piece_justificative_0) { create(:type_de_piece_justificative, procedure: procedure, order_place: 0) } let!(:piece_justificative_1) { create(:type_de_piece_justificative, procedure: procedure, order_place: 1) } let(:received_mail){ create(:received_mail) } @@ -174,6 +176,8 @@ describe Procedure do expect(subject.types_de_piece_justificative.size).to eq procedure.types_de_piece_justificative.size expect(subject.types_de_champ.size).to eq procedure.types_de_champ.size expect(subject.types_de_champ_private.size).to eq procedure.types_de_champ_private.size + expect(subject.types_de_champ.map(&:drop_down_list).compact.size).to eq procedure.types_de_champ.map(&:drop_down_list).compact.size + expect(subject.types_de_champ_private.map(&:drop_down_list).compact.size).to eq procedure.types_de_champ_private.map(&:drop_down_list).compact.size subject.types_de_champ.zip(procedure.types_de_champ).each do |stc, ptc| expect(stc).to have_same_attributes_as(ptc) From ee415c4aa612bf389800de356501f914f3268147 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Wed, 28 Jun 2017 10:51:46 +0200 Subject: [PATCH 098/136] [FIX #509] add dossier id tag in attestation template --- app/models/attestation_template.rb | 3 ++- spec/models/attestation_template_spec.rb | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/models/attestation_template.rb b/app/models/attestation_template.rb index 9c2b5ad49..3e84c0108 100644 --- a/app/models/attestation_template.rb +++ b/app/models/attestation_template.rb @@ -59,7 +59,8 @@ class AttestationTemplate < ApplicationRecord end def dossier_tags - [{ libelle: 'motivation', description: '', target: 'motivation' }] + [{ libelle: 'motivation', description: '', target: 'motivation' }, + { libelle: 'numéro du dossier', description: '', target: 'id' }] end def individual_tags diff --git a/spec/models/attestation_template_spec.rb b/spec/models/attestation_template_spec.rb index 95270dbb9..6d7dffb7c 100644 --- a/spec/models/attestation_template_spec.rb +++ b/spec/models/attestation_template_spec.rb @@ -230,10 +230,10 @@ describe AttestationTemplate, type: :model do context 'when the dossier has a motivation' do let(:dossier) { create(:dossier, motivation: 'motivation') } - context 'and the title has the motivation tag' do - let(:template_title) { 'title --motivation--' } + context 'and the title has some dossier tags' do + let(:template_title) { 'title --motivation-- --numéro du dossier--' } - it { expect(view_args[:title]).to eq('title motivation') } + it { expect(view_args[:title]).to eq("title motivation #{dossier.id}") } end end From 26afa24e49c6c1cbae55dbbcc21b7bcba9e52836 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Wed, 28 Jun 2017 16:47:57 +0200 Subject: [PATCH 099/136] [Fix #512] Crisp: add crisp to homepage --- app/assets/stylesheets/new_design/landing.scss | 2 ++ app/views/layouts/_crisp.html.haml | 15 +++++++++++++++ app/views/layouts/new_application.html.haml | 1 + app/views/root/landing.html.haml | 12 ++---------- 4 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 app/views/layouts/_crisp.html.haml diff --git a/app/assets/stylesheets/new_design/landing.scss b/app/assets/stylesheets/new_design/landing.scss index b5fa9cb12..769f49622 100644 --- a/app/assets/stylesheets/new_design/landing.scss +++ b/app/assets/stylesheets/new_design/landing.scss @@ -70,6 +70,7 @@ $landing-breakpoint: 1040px; color: #FFFFFF; font-size: 24px; margin-top: 30px; + cursor: pointer; &:hover { color: #FFFFFF; @@ -286,6 +287,7 @@ $cta-panel-button-border-size: 2px; color: #FFFFFF; font-size: 24px; text-align: center; + cursor: pointer; &:hover { color: #FFFFFF; diff --git a/app/views/layouts/_crisp.html.haml b/app/views/layouts/_crisp.html.haml new file mode 100644 index 000000000..d16217cd0 --- /dev/null +++ b/app/views/layouts/_crisp.html.haml @@ -0,0 +1,15 @@ +- if request.path == root_path + :javascript + window.$crisp=[]; + window.CRISP_WEBSITE_ID="779b5050-4cc1-4172-8dd0-bde55716a289"; + (function(){ + d=document; + s=d.createElement("script"); + s.src="https://client.crisp.im/l.js"; + s.async=1; + d.getElementsByTagName("head")[0].appendChild(s); + })(); + window.$crisp.push(["do", "chat:show"]); +- else + :javascript + window.$crisp && window.$crisp.push(["do", "chat:hide"]); diff --git a/app/views/layouts/new_application.html.haml b/app/views/layouts/new_application.html.haml index e0e9f4dce..b12804452 100644 --- a/app/views/layouts/new_application.html.haml +++ b/app/views/layouts/new_application.html.haml @@ -35,6 +35,7 @@ = render partial: "layouts/new_footer" = render partial: "layouts/google_analytics" = render partial: "layouts/mailjet_newsletter" + = render partial: "layouts/crisp" = javascript_include_tag "application", "data-turbolinks-track": true = yield :charts_js diff --git a/app/views/root/landing.html.haml b/app/views/root/landing.html.haml index ddff3b458..875e9d908 100644 --- a/app/views/root/landing.html.haml +++ b/app/views/root/landing.html.haml @@ -8,11 +8,7 @@ %br vos procédures administratives en quelques minutes - = link_to "Demander une démo", - "mailto:#{t("dynamics.contact_email")}?subject=Demande de démo TPS", - class: "hero-button", - target: "_blank", - onclick: "javascript: ga('send', 'pageview', '/demander-une-demo')" + %a.hero-button{ target: "_blank", onclick: "javascript: ga('send', 'pageview', '/demander-une-demo'); $crisp.push(['do', 'chat:open'])" } Demander une démo %p.hero-phone-cta ou nous appeler au 01 40 15 68 49 @@ -132,10 +128,6 @@ %h1.cta-panel-title Commencez à dématerialiser vos procédures %p.cta-panel-explanation Nous vous accompagnons dans la prise en main de l’outil %div - = link_to "Demander une démo", - "mailto:#{t('dynamics.contact_email')}?subject=Demande de démo TPS", - class: "cta-panel-button", - target: "_blank", - onclick: "javascript: ga('send', 'pageview', '/demander-une-demo')" + %a.cta-panel-button{ target: "_blank", onclick: "javascript: ga('send', 'pageview', '/demander-une-demo'); $crisp.push(['do', 'chat:open'])" } Demander une démo %p.cta-panel-phone-cta ou nous appeler au 01 40 15 68 49 From 689d0c2dd7e38117e6f98e223bf68bb2b1e683d0 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Tue, 27 Jun 2017 17:58:16 +0200 Subject: [PATCH 100/136] ProcedureOverview: add layout --- .../gestionnaire_mailer/logo-beta-gouv-fr.png | Bin 0 -> 1979 bytes app/mailers/gestionnaire_mailer.rb | 2 + app/views/layouts/mailers/layout.html.erb | 190 ++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 app/assets/images/mailer/gestionnaire_mailer/logo-beta-gouv-fr.png create mode 100644 app/views/layouts/mailers/layout.html.erb diff --git a/app/assets/images/mailer/gestionnaire_mailer/logo-beta-gouv-fr.png b/app/assets/images/mailer/gestionnaire_mailer/logo-beta-gouv-fr.png new file mode 100644 index 0000000000000000000000000000000000000000..7cb725c37de695c23ef658c6669b9decd5fd7f9c GIT binary patch literal 1979 zcmV;s2SoUZP)^S zpxN1;nV$KZBWqc6U3c-K7hPI#!;6b<_+ZfsU&z&Jiy9}(gz>89tBos9De&lw0X5Hzf)@0Q%~ziF?ktQ88;lA{0a z1ur^Pz4JV8X@F&$ZMK;&1I$D%Yohl@ME?VGxg8cvnwtS81IrGv{_jn-kO7l(YpaNR z+F8iYi}L($(Fy;N`1X?UjlJ8pZ9A8Jm-FY8HEm_hQ__Et-bMO%((f#~HP^sj&Mi98 z^Yrb(!ej1`?;X$cK)ghrGiC3O;yr)G0mWr*T6Fvp)}WW%=)|rZrFBSb*b^i)(l?ad^Ij|KaqIXm9P=cfX+Ww&T|(GBhHjM z6rJFlf)_lD`vw3NF_!0-kv$P$s0(Y371tBb+F@EL0c6A0BJ>Y5XMiZ|0a<@lY;hxK z>^V}&dz_5@8OzkeAoJVxSys5J%M1Jy9lg$prOaH9QWUW0t+26!K2Q*uvdGhmQ= z?`sKLkEuEMn0gP6quv9x0F&Mkvk4f_CIbc+$KEA4(jwD?DFK6h`x3wpF--y=FaZXs zC&~K*LWbcY0`@*gQ6~p5Xeg%9cb~+&TrM{cbVhg5P$yOp73PK!7GZG3Xjy$c_Tr z-{CM(sD|O>0tS~V?}s&Ww`#-MFl^fIt8|>T<|?jbBGCXkJeC;LZN_RZ^8`#VD3$vqh`VDNmgP?$xgozeiNSlnh2-WTE00>oV`fO(1lbFTF}L{x=5Ze;+> zNLa15<^UqiuNq{!mDb-W{l}ItAGJ@JPRlW zcQYxiHm;sVa2sI2o8U6(aynQ-#|>#Y3~>zf(@k@bAuwP}p#d41#S?G9Mg;FjUczbw z;0Byg>idGI!n>$_X|KP)>Cyov`ofk@1FRSWkEtuI#>KU9f^`iRH-=*W{)EWeX#MNu za(Mw>i}Er!2o78aU|6PmY2PlTYHfWU2YH6;v;hW6gjMTx3Di#5(nbDb6c<6(!Q(n$ zP@`czfV1&s!C>I#EujU*)t%RJwu3ql>Eg6R9?ASJMFXb-sYloZm*KI^e1EYxTkVHP zlLQ#tnacax*jMc#j0YaCSAI9+?<0RKNIC z?W?Src-L>*+EJKC0Mr1(fD5nHIa!=>ysgz{hN8Hr8Q$euM*5nBg}af~^SYw%uVncn z;pUorJ#_tB=8Gnxtky2$ce&8BaNtR1AWm=r zGALR%7DX)_0Itx0=uMM;-hj)+1+f={B0v%gZnS;N`rvl0cJw6XB3^^+TK<%QPAb1>;N;oBF?XSC3*h=dOA*|Sp>VhGJ$fcExeY}u^Pk-9m!r#t+KVXV zP;?0gMbkG>W1%ePgnF_suF0g-RqH9KBL;wxGbW#jTh>rG)WXkG$WTCj<^R`fCH_Io ze`fXu4@w2ctfj~jzN@3iIEuN?%-9gJG_v25`AOF5YzVXX>#PMh!{LSU6@Rmt_k{E_ zODFOWikrx8aNuNan>BEVCZZeU@Gr;ynYB^*|2I^1@yh}H-eyYeJpuc|QLqSE?%DtV N002ovPDHLkV1k}w%6 + + + + <%= yield(:title) %> + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + +
+
+ Logo TPS +
+
+
+ +
+ + + + + + +
+
+

+

+
+
+
+ +
+
+ + +
+ + + + + + +
+ +
+ + + + + + +
+
+

<%= yield(:title) %>

+ <%= yield %> +
+
+
+ +
+
+ + +
+ + + + + + +
+ +
+ + + + + + + + + +
+
+ Logo Beta Gouv Fr +
+
+
+

+

+
+
+
+ +
+
+ +
+ + From 603e2dd0209d3da7d37c995857f3f1f1ddd3cd03 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Tue, 27 Jun 2017 18:00:05 +0200 Subject: [PATCH 101/136] ProcedureOverview: reboot model and layout --- app/models/gestionnaire.rb | 5 +- app/models/procedure.rb | 4 +- app/models/procedure_overview.rb | 102 ++++++++---------- .../last_week_overview.html.haml | 68 +++++++----- spec/models/gestionnaire_spec.rb | 8 -- spec/models/procedure_overview_spec.rb | 89 +++++++-------- 6 files changed, 138 insertions(+), 138 deletions(-) diff --git a/app/models/gestionnaire.rb b/app/models/gestionnaire.rb index 2b971e7d1..c2252d18a 100644 --- a/app/models/gestionnaire.rb +++ b/app/models/gestionnaire.rb @@ -106,16 +106,15 @@ class Gestionnaire < ActiveRecord::Base active_procedure_overviews = procedures .where(published: true) .all - .map { |procedure| procedure.procedure_overview(start_date, dossiers_with_notifications_count_for_procedure(procedure)) } + .map { |procedure| procedure.procedure_overview(start_date) } .select(&:had_some_activities?) - if active_procedure_overviews.count == 0 && notifications.count == 0 + if active_procedure_overviews.count == 0 nil else { start_date: start_date, procedure_overviews: active_procedure_overviews, - notifications: notifications } end end diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 8db90cdef..f55595eea 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -135,8 +135,8 @@ class Procedure < ActiveRecord::Base } end - def procedure_overview(start_date, notifications_count) - ProcedureOverview.new(self, start_date, notifications_count) + def procedure_overview(start_date) + ProcedureOverview.new(self, start_date) end def initiated_mail_template diff --git a/app/models/procedure_overview.rb b/app/models/procedure_overview.rb index 464001cf2..c614bf4a3 100644 --- a/app/models/procedure_overview.rb +++ b/app/models/procedure_overview.rb @@ -1,82 +1,72 @@ class ProcedureOverview - include Rails.application.routes.url_helpers - attr_accessor :libelle, :notifications_count, :received_dossiers_count, :created_dossiers_count, :processed_dossiers_count, :date + attr_accessor :procedure, + :created_dossiers_count, + :dossiers_en_instruction_count, + :old_dossiers_en_instruction, + :dossiers_en_construction_count, + :old_dossiers_en_construction - def initialize(procedure, start_date, notifications_count) - @libelle = procedure.libelle - @procedure_url = backoffice_dossiers_procedure_url(procedure) - @notifications_count = notifications_count + def initialize(procedure, start_date) + @start_date = start_date + @procedure = procedure - @received_dossiers_count = procedure.dossiers.where(state: :received).count - @created_dossiers_count = procedure.dossiers + @dossiers_en_instruction_count = procedure.dossiers.state_en_instruction.count + @old_dossiers_en_instruction = procedure + .dossiers + .state_en_instruction + .where('received_at < ?', 1.week.ago) + + @dossiers_en_construction_count = procedure.dossiers.state_en_construction.count + @old_dossiers_en_construction = procedure + .dossiers + .state_en_construction + .where('initiated_at < ?', 1.week.ago) + + @created_dossiers_count = procedure + .dossiers .where(created_at: start_date..DateTime.now) - .where.not(state: :draft) + .state_not_brouillon .count - @processed_dossiers_count = procedure.dossiers.where(processed_at: start_date..DateTime.now).count end def had_some_activities? - [received_dossiers_count, - created_dossiers_count, - processed_dossiers_count, - notifications_count].reduce(:+) > 0 + [@dossiers_en_instruction_count, + @dossiers_en_construction_count, + @created_dossiers_count].reduce(:+) > 0 end - def to_html - [libelle_description, - dossiers_en_instruction_description, - created_dossier_description, - processed_dossier_description, - notifications_description].compact.join('
') - end - - private - - def libelle_description - "
#{libelle}" - end - - def dossiers_en_instruction_description - case received_dossiers_count + def dossiers_en_construction_description + case @dossiers_en_construction_count when 0 nil when 1 - "1 dossier est en cours d'instruction" + 'dossier suivi en construction' else - "#{received_dossiers_count} dossiers sont en cours d'instruction" + 'dossiers suivis en construction' + end + end + + def dossiers_en_instruction_description + case @dossiers_en_instruction_count + when 0 + nil + when 1 + "dossier est en cours d'instruction" + else + "dossiers sont en cours d'instruction" end end def created_dossier_description - case created_dossiers_count - when 0 - nil - when 1 - '1 nouveau dossier a été déposé' - else - "#{created_dossiers_count} nouveaux dossiers ont été déposés" - end - end + formated_date = I18n.l(@start_date, format: '%d %B %Y') - def processed_dossier_description - case processed_dossiers_count + case @created_dossiers_count when 0 nil when 1 - '1 dossier a été instruit' + "nouveau dossier a été déposé depuis le #{formated_date}" else - "#{processed_dossiers_count} dossiers ont été instruits" - end - end - - def notifications_description - case notifications_count - when 0 - nil - when 1 - '1 notification en attente sur les dossiers que vous suivez' - else - "#{notifications_count} notifications en attente sur les dossiers que vous suivez" + "nouveaux dossiers ont été déposés depuis le #{formated_date}" end end end diff --git a/app/views/gestionnaire_mailer/last_week_overview.html.haml b/app/views/gestionnaire_mailer/last_week_overview.html.haml index a82bb9497..8dab014f9 100644 --- a/app/views/gestionnaire_mailer/last_week_overview.html.haml +++ b/app/views/gestionnaire_mailer/last_week_overview.html.haml @@ -1,26 +1,44 @@ -%table{ align: 'center', border: '0', cellpadding: '0', cellspacing: '0', height: '100%', style: 'background-color: #fafafa', width: '100%' } - %tbody - %tr - %td{ align: 'center', style: 'height: 100%; margin: 0; padding: 30px; width: 100%; border-top: 0', valign: 'top' } - %table{ border: '0', cellpadding: '0', cellspacing: '0', style: 'border-collapse: collapse; border: 0; max-width: 600px!important;', width: '100%' } - %tbody - %tr - %td{ style: 'background: #ffffff none no-repeat center/cover; background-color: #ffffff; background-image: none; background-repeat: no-repeat; background-position: center; background-size: cover; border-top: 0; padding-top: 0;', valign: 'top' } - %table{ border: '0', cellpadding: '0', cellspacing: '0', style: 'min-width: 100%; border-collapse: collapse', width: '100%' } - %tr - %td{ style: 'padding: 0 30px; mso-line-height-rule: exactly; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; ', valign: 'top' } - %img{ align: 'middle', alt: 'Logo TPS', src: image_url('mailer/gestionnaire_mailer/logo.png'), style: 'max-width: 125px; padding: 30px 0; display: inline !important; vertical-align: bottom; border: 0; height: auto; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic;' } - %tr - %td{ style: 'padding: 0 30px 30px; word-break: break-word; color: #333333; font-family: Helvetica; font-size: 16px; line-height: 150%; text-align: left; border-bottom: 2px solid #4393F3;', valign: 'top' } - Bonjour, voici votre résumé de l'activité de la semaine du #{l(@args[:start_date], format: '%d %B')} au #{l(DateTime.now, format: '%d %B')}. - %br - %br +- content_for(:title, 'Vos activités sur TPS') - - @args[:procedure_overviews].each do |procedure_overview| - = procedure_overview.to_html.html_safe - %br - %br - Bonne journée, - %br - %br - L'équipe Téléprocédures Simplifiées +- @args[:procedure_overviews].each_with_index do |procedure_overview, index| + + %h2{ style: 'font-size: 20px; font-weight: 300; margin: 25px 0 5px;' } + #{procedure_overview.procedure.libelle} + = link_to 'voir', backoffice_dossiers_procedure_url(procedure_overview.procedure), style: 'color: #4393F3; font-size: 14px;' + %table{ cellpadding: '0', cellspacing: '0', style: 'width: 100%; padding-bottom: 20px;' } + %tbody + + - if procedure_overview.created_dossiers_count > 0 + %tr + %td{ style: 'padding: 2px 0;' } + %span{ style: 'font-weight: bold;' }= procedure_overview.created_dossiers_count + = procedure_overview.created_dossier_description + + - if procedure_overview.dossiers_en_construction_count > 0 + %tr + %td{ style: 'padding: 2px 0;' } + %span{ style: 'font-weight: bold;' }= procedure_overview.dossiers_en_construction_count + = procedure_overview.dossiers_en_construction_description + - if procedure_overview.old_dossiers_en_construction.count > 0 + dont #{procedure_overview.old_dossiers_en_construction.count} depuis plus de 7 jours + - if procedure_overview.old_dossiers_en_construction.count < 6 + \: + = procedure_overview.old_dossiers_en_construction.map do |old_dossier| + - link_to "nº #{old_dossier.id}", backoffice_dossier_url(old_dossier), style: 'color: #4393F3;' + - end.join(', ').html_safe + + - if procedure_overview.dossiers_en_instruction_count > 0 + %tr + %td{ style: 'padding: 2px 0;' } + %span{ style: 'font-weight: bold;' }= procedure_overview.dossiers_en_instruction_count + = procedure_overview.dossiers_en_instruction_description + - if procedure_overview.old_dossiers_en_instruction.count > 0 + dont #{procedure_overview.old_dossiers_en_instruction.count} depuis plus de 7 jours + - if procedure_overview.old_dossiers_en_instruction.count < 6 + \: + = procedure_overview.old_dossiers_en_instruction.each do |old_dossier| + - link_to "nº #{old_dossier.id}", backoffice_dossier_url(old_dossier), style: 'color: #4393F3;' + - end.join(', ').html_safe + + - if index != (@args[:procedure_overviews].count - 1) + .spacer{ style: 'border-bottom: 1px solid #CCC; margin: 25px 0 30px;' } diff --git a/spec/models/gestionnaire_spec.rb b/spec/models/gestionnaire_spec.rb index dc2679604..b55a26684 100644 --- a/spec/models/gestionnaire_spec.rb +++ b/spec/models/gestionnaire_spec.rb @@ -365,14 +365,6 @@ describe Gestionnaire, type: :model do context 'when the gestionnaire has no notifications' do it { is_expected.to eq(nil) } end - - context 'when the gestionnaire has one notification' do - before :each do - expect(gestionnaire2).to receive(:notifications).twice.and_return([1]) - end - - it { is_expected.to eq({ start_date: monday, procedure_overviews: [], notifications: [1] }) } - end end context 'when a procedure published was active' do diff --git a/spec/models/procedure_overview_spec.rb b/spec/models/procedure_overview_spec.rb index defe51345..2ab2f8c9d 100644 --- a/spec/models/procedure_overview_spec.rb +++ b/spec/models/procedure_overview_spec.rb @@ -9,14 +9,36 @@ describe ProcedureOverview, type: :model do Timecop.freeze(friday) end - let(:procedure_overview) { ProcedureOverview.new(procedure, monday, 0) } + let(:procedure_overview) { ProcedureOverview.new(procedure, monday) } - describe 'received_dossiers_count' do - let!(:received_dossier) do - dossier = create(:dossier, procedure: procedure, state: :received, created_at: monday) + describe 'dossiers_en_instruction_count' do + let!(:en_instruction_dossier) do + create(:dossier, procedure: procedure, state: :received, created_at: monday) end - it { expect(procedure_overview.received_dossiers_count).to eq(1) } + it { expect(procedure_overview.dossiers_en_instruction_count).to eq(1) } + end + + describe 'old_dossiers_en_instruction' do + let!(:old_dossier_en_instruction) do + create(:dossier, procedure: procedure, state: :received, received_at: monday - 1.month) + end + + let!(:dossier_en_instruction) do + create(:dossier, procedure: procedure, state: :received, received_at: monday) + end + + it do + expect(procedure_overview.old_dossiers_en_instruction).to match([old_dossier_en_instruction]) + end + end + + describe 'dossiers_en_construction_count' do + let!(:dossier_en_construction) do + create(:dossier, procedure: procedure, state: :initiated, created_at: monday) + end + + it { expect(procedure_overview.dossiers_en_construction_count).to eq(1) } end describe 'created_dossiers_count' do @@ -35,53 +57,32 @@ describe ProcedureOverview, type: :model do it { expect(procedure_overview.created_dossiers_count).to eq(1) } end - describe 'processed_dossiers_count' do - let!(:processed_dossier_during_the_week) do - create(:dossier, procedure: procedure, created_at: monday, processed_at: monday) + describe 'had_some_activities?' do + subject { procedure_overview.had_some_activities? } + + before :each do + procedure_overview.dossiers_en_instruction_count = 0 + procedure_overview.dossiers_en_construction_count = 0 + procedure_overview.created_dossiers_count = 0 end - let!(:processed_dossier_before_the_week) do - create(:dossier, procedure: procedure, created_at: (monday - 1.week), processed_at: (monday - 1.week)) + context 'when there are no activities' do + it { is_expected.to be false } end - it { expect(procedure_overview.processed_dossiers_count).to eq(1) } - end - - describe 'to_html' do - subject { procedure_overview.to_html } - - context 'when the different count are equal to 0' do - it { is_expected.to match(/^libelle<\/strong><\/a>$/) } + context 'when there are some dossiers en instruction' do + before { procedure_overview.dossiers_en_instruction_count = 2 } + it { is_expected.to be true } end - context 'when the different counts are equal to 1' do - before :each do - procedure_overview.notifications_count = 1 - procedure_overview.received_dossiers_count = 1 - procedure_overview.created_dossiers_count = 1 - procedure_overview.processed_dossiers_count = 1 - end - - it { is_expected.to match(/^libelle<\/strong><\/a>/) } - it { is_expected.to include("1 dossier est en cours d'instruction") } - it { is_expected.to include('1 nouveau dossier a été déposé') } - it { is_expected.to include('1 dossier a été instruit') } - it { is_expected.to include('1 notification en attente sur les dossiers que vous suivez') } + context 'when there are some dossiers en construction' do + before { procedure_overview.dossiers_en_construction_count = 2 } + it { is_expected.to be true } end - context 'when the different counts are equal to 2' do - before :each do - procedure_overview.notifications_count = 2 - procedure_overview.received_dossiers_count = 3 - procedure_overview.created_dossiers_count = 4 - procedure_overview.processed_dossiers_count = 5 - end - - it { is_expected.to match(/^libelle<\/strong><\/a>/) } - it { is_expected.to include("3 dossiers sont en cours d'instruction") } - it { is_expected.to include('4 nouveaux dossiers ont été déposés') } - it { is_expected.to include('5 dossiers ont été instruits') } - it { is_expected.to include('2 notifications en attente sur les dossiers que vous suivez') } + context 'when there are some created dossiers' do + before { procedure_overview.created_dossiers_count = 2 } + it { is_expected.to be true } end end end From 149c1ba459c7d59dcb46fef86ef8aaaf6998fc28 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Tue, 27 Jun 2017 18:00:48 +0200 Subject: [PATCH 102/136] ProcedureOverview: change email title --- app/mailers/gestionnaire_mailer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/mailers/gestionnaire_mailer.rb b/app/mailers/gestionnaire_mailer.rb index bbc40cb12..ac8124191 100644 --- a/app/mailers/gestionnaire_mailer.rb +++ b/app/mailers/gestionnaire_mailer.rb @@ -7,7 +7,7 @@ class GestionnaireMailer < ApplicationMailer def last_week_overview(gestionnaire, overview) headers['X-mailjet-campaign'] = 'last_week_overview' - send_mail gestionnaire.email, overview, 'Résumé de la semaine' + send_mail gestionnaire.email, overview, 'Vos activités sur TPS' end private From fb50606680383832af6d744588906e20cceb12d9 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Fri, 30 Jun 2017 17:21:41 +0200 Subject: [PATCH 103/136] Admin: remove useless data in admin creation mail --- app/controllers/administrations_controller.rb | 2 +- app/mailers/new_admin_mailer.rb | 3 +-- app/views/new_admin_mailer/new_admin_email.text.erb | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/controllers/administrations_controller.rb b/app/controllers/administrations_controller.rb index 26ebc9087..8a36c6fc5 100644 --- a/app/controllers/administrations_controller.rb +++ b/app/controllers/administrations_controller.rb @@ -18,7 +18,7 @@ class AdministrationsController < ApplicationController if admin.save flash.notice = "Administrateur créé" - NewAdminMailer.new_admin_email(admin, params[:administrateur][:password]).deliver_now! + NewAdminMailer.new_admin_email(admin).deliver_now! else flash.alert = admin.errors.full_messages.join('
').html_safe end diff --git a/app/mailers/new_admin_mailer.rb b/app/mailers/new_admin_mailer.rb index 673b2a53a..71ea69b4f 100644 --- a/app/mailers/new_admin_mailer.rb +++ b/app/mailers/new_admin_mailer.rb @@ -1,7 +1,6 @@ class NewAdminMailer < ApplicationMailer - def new_admin_email admin, password + def new_admin_email admin @admin = admin - @password = password mail(to: 'tech@tps.apientreprise.fr', subject: "Création d'un compte Admin TPS") diff --git a/app/views/new_admin_mailer/new_admin_email.text.erb b/app/views/new_admin_mailer/new_admin_email.text.erb index 678123a26..e0946ef9a 100644 --- a/app/views/new_admin_mailer/new_admin_email.text.erb +++ b/app/views/new_admin_mailer/new_admin_email.text.erb @@ -3,7 +3,6 @@ Un nouvel administrateur a été créé sur TPS. Plateforme : <%= TPS::Application::URL %> Login : <%= @admin.email %> -Password : <%= @password %> Bonne journée, From 53b94e0017836cb8bd7af3174477c1ab02d6ba04 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 27 Jun 2017 17:00:31 +0200 Subject: [PATCH 104/136] Remove tests that test nothing --- spec/models/procedure_spec.rb | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 69f15588a..02ad13cd0 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -247,16 +247,8 @@ describe Procedure do procedure.reload end - it 'is not available from a valid path anymore' do - expect(procedure.path).to eq procedure_path.path - expect(procedure.published).to be_truthy - expect(procedure.archived).to be_truthy - end - - it 'is not in ProcedurePath table anymore' do - expect(ProcedurePath.where(path: procedure.path).count).to eq(1) - expect(ProcedurePath.find_by_procedure_id(procedure.id)).not_to be_nil - end + it { expect(procedure.published).to be_truthy } + it { expect(procedure.archived).to be_truthy } end describe 'total_dossier' do From 28f2ec1d860483b0231eaee0fcd00c50bb9a0d59 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Thu, 29 Jun 2017 12:20:27 +0200 Subject: [PATCH 105/136] Reorder some tests --- .../auto_archive_procedure_worker_spec.rb | 58 ++++++++----------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/spec/workers/auto_archive_procedure_worker_spec.rb b/spec/workers/auto_archive_procedure_worker_spec.rb index 6e0743477..4fb891ded 100644 --- a/spec/workers/auto_archive_procedure_worker_spec.rb +++ b/spec/workers/auto_archive_procedure_worker_spec.rb @@ -18,43 +18,35 @@ RSpec.describe AutoArchiveProcedureWorker, type: :worker do end context "when procedures have auto_archive_on set on yesterday or today" do - describe "titi" do - before do - subject - procedure_hier.reload - procedure_aujourdhui.reload - end + let!(:dossier1) { create(:dossier, procedure: procedure_hier, state: 'draft', archived: false)} + let!(:dossier2) { create(:dossier, procedure: procedure_hier, state: 'initiated', archived: false)} + let!(:dossier3) { create(:dossier, procedure: procedure_hier, state: 'replied', archived: false)} + let!(:dossier4) { create(:dossier, procedure: procedure_hier, state: 'updated', archived: false)} + let!(:dossier5) { create(:dossier, procedure: procedure_hier, state: 'received', archived: false)} + let!(:dossier6) { create(:dossier, procedure: procedure_hier, state: 'closed', archived: false)} + let!(:dossier7) { create(:dossier, procedure: procedure_hier, state: 'refused', archived: false)} + let!(:dossier8) { create(:dossier, procedure: procedure_hier, state: 'without_continuation', archived: false)} - it { expect(procedure_hier.archived).to eq true } - it { expect(procedure_aujourdhui.archived).to eq true } + before do + subject + (1..8).each do |i| + eval "dossier#{i}.reload" + end + procedure_hier.reload + procedure_aujourdhui.reload end - context "with dossiers" do - let!(:dossier1) { create(:dossier, procedure: procedure_hier, state: 'draft', archived: false)} - let!(:dossier2) { create(:dossier, procedure: procedure_hier, state: 'initiated', archived: false)} - let!(:dossier3) { create(:dossier, procedure: procedure_hier, state: 'replied', archived: false)} - let!(:dossier4) { create(:dossier, procedure: procedure_hier, state: 'updated', archived: false)} - let!(:dossier5) { create(:dossier, procedure: procedure_hier, state: 'received', archived: false)} - let!(:dossier6) { create(:dossier, procedure: procedure_hier, state: 'closed', archived: false)} - let!(:dossier7) { create(:dossier, procedure: procedure_hier, state: 'refused', archived: false)} - let!(:dossier8) { create(:dossier, procedure: procedure_hier, state: 'without_continuation', archived: false)} + it { expect(dossier1.state).to eq 'draft' } + it { expect(dossier2.state).to eq 'received' } + it { expect(dossier3.state).to eq 'received' } + it { expect(dossier4.state).to eq 'received' } + it { expect(dossier5.state).to eq 'received' } + it { expect(dossier6.state).to eq 'closed' } + it { expect(dossier7.state).to eq 'refused' } + it { expect(dossier8.state).to eq 'without_continuation' } - before do - subject - (1..8).each do |i| - eval "dossier#{i}.reload" - end - end - - it { expect(dossier1.state).to eq 'draft' } - it { expect(dossier2.state).to eq 'received' } - it { expect(dossier3.state).to eq 'received' } - it { expect(dossier4.state).to eq 'received' } - it { expect(dossier5.state).to eq 'received' } - it { expect(dossier6.state).to eq 'closed' } - it { expect(dossier7.state).to eq 'refused' } - it { expect(dossier8.state).to eq 'without_continuation' } - end + it { expect(procedure_hier.archived).to eq true } + it { expect(procedure_aujourdhui.archived).to eq true } end context "when procedures have auto_archive_on set on future" do From 0ec3855f8da76cf6d005bfc8ade7ac04d27a9376 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Thu, 29 Jun 2017 15:07:33 +0200 Subject: [PATCH 106/136] Add a missing test --- spec/workers/auto_archive_procedure_worker_spec.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spec/workers/auto_archive_procedure_worker_spec.rb b/spec/workers/auto_archive_procedure_worker_spec.rb index 4fb891ded..7a9c2a380 100644 --- a/spec/workers/auto_archive_procedure_worker_spec.rb +++ b/spec/workers/auto_archive_procedure_worker_spec.rb @@ -26,12 +26,15 @@ RSpec.describe AutoArchiveProcedureWorker, type: :worker do let!(:dossier6) { create(:dossier, procedure: procedure_hier, state: 'closed', archived: false)} let!(:dossier7) { create(:dossier, procedure: procedure_hier, state: 'refused', archived: false)} let!(:dossier8) { create(:dossier, procedure: procedure_hier, state: 'without_continuation', archived: false)} + let!(:dossier9) { create(:dossier, procedure: procedure_aujourdhui, state: 'initiated', archived: false)} before do subject - (1..8).each do |i| + + (1..9).each do |i| eval "dossier#{i}.reload" end + procedure_hier.reload procedure_aujourdhui.reload end @@ -44,6 +47,7 @@ RSpec.describe AutoArchiveProcedureWorker, type: :worker do it { expect(dossier6.state).to eq 'closed' } it { expect(dossier7.state).to eq 'refused' } it { expect(dossier8.state).to eq 'without_continuation' } + it { expect(dossier9.state).to eq 'received' } it { expect(procedure_hier.archived).to eq true } it { expect(procedure_aujourdhui.archived).to eq true } From cf573a1ec6df27ca3566ae6225f1fed8a14fc722 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 27 Jun 2017 16:57:25 +0200 Subject: [PATCH 107/136] Use Procedure#archive instead of duplicating code --- app/workers/auto_archive_procedure_worker.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/workers/auto_archive_procedure_worker.rb b/app/workers/auto_archive_procedure_worker.rb index 54875a961..a1a46efcc 100644 --- a/app/workers/auto_archive_procedure_worker.rb +++ b/app/workers/auto_archive_procedure_worker.rb @@ -7,7 +7,7 @@ class AutoArchiveProcedureWorker dossier.received! end - procedure.update_attributes!(archived: true) + procedure.archive end end end From 698bffd1562b08be2f85dbaa10354487a7ffffae Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 27 Jun 2017 16:49:52 +0200 Subject: [PATCH 108/136] [Ref #145] Add the AddArchivedAtToProcedures --- db/migrate/20170627143701_add_archived_at_to_procedure.rb | 5 +++++ db/schema.rb | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20170627143701_add_archived_at_to_procedure.rb diff --git a/db/migrate/20170627143701_add_archived_at_to_procedure.rb b/db/migrate/20170627143701_add_archived_at_to_procedure.rb new file mode 100644 index 000000000..ba1bf7890 --- /dev/null +++ b/db/migrate/20170627143701_add_archived_at_to_procedure.rb @@ -0,0 +1,5 @@ +class AddArchivedAtToProcedure < ActiveRecord::Migration[5.0] + def change + add_column :procedures, :archived_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index cd1760bc5..ad99eb400 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170627091953) do +ActiveRecord::Schema.define(version: 20170627143701) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -383,6 +383,7 @@ ActiveRecord::Schema.define(version: 20170627091953) do t.boolean "individual_with_siret", default: false t.datetime "auto_archive_on" t.datetime "published_at" + t.datetime "archived_at" end create_table "quartier_prioritaires", force: :cascade do |t| From 6b90e0ccfad8c2fa36e2a601dfe9bf5b3001e93a Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 27 Jun 2017 17:05:49 +0200 Subject: [PATCH 109/136] [Ref #145] Record archived_at when archiving a procedure --- app/models/procedure.rb | 2 +- spec/models/procedure_spec.rb | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/models/procedure.rb b/app/models/procedure.rb index f55595eea..8b6481708 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -116,7 +116,7 @@ class Procedure < ActiveRecord::Base end def archive - self.update_attributes!({archived: true}) + self.update_attributes!(archived: true, archived_at: Time.now) end def total_dossier diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 02ad13cd0..9de535d08 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -227,8 +227,8 @@ describe Procedure do let(:procedure) { create(:procedure) } before do - procedure.publish!("example-path") Timecop.freeze(Time.now) + procedure.publish!("example-path") end it { expect(procedure.published).to eq(true) } @@ -243,12 +243,14 @@ describe Procedure do let(:procedure) { create(:procedure, :published) } let(:procedure_path) { ProcedurePath.find(procedure.procedure_path.id) } before do + Timecop.freeze(Time.now) procedure.archive procedure.reload end it { expect(procedure.published).to be_truthy } it { expect(procedure.archived).to be_truthy } + it { expect(procedure.archived_at).to eq(Time.now) } end describe 'total_dossier' do From 0a1543c630ba98e3d97206eaea349f7caed1778d Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 3 Jul 2017 09:20:37 +0200 Subject: [PATCH 110/136] [Fix #523] prioritaites -> prioritaires --- app/views/users/carte/_map.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/users/carte/_map.html.haml b/app/views/users/carte/_map.html.haml index 80f1bbf36..edefdda66 100644 --- a/app/views/users/carte/_map.html.haml +++ b/app/views/users/carte/_map.html.haml @@ -3,7 +3,7 @@ - if dossier.procedure.module_api_carto.quartiers_prioritaires .col-md-9.col-lg-9#qp.col-md-3.col-lg-3.list - %h3.text-info Quartiers prioritaites + %h3.text-info Quartiers prioritaires %ul - if dossier.procedure.module_api_carto.cadastre From 38400cc2ce74a1d9a6f4682aa6e902caa59a0fcc Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Mon, 3 Jul 2017 09:21:04 +0200 Subject: [PATCH 111/136] Fix a capitalization mistake --- app/views/admin/procedures/_informations.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/procedures/_informations.html.haml b/app/views/admin/procedures/_informations.html.haml index 253ee6729..706f90135 100644 --- a/app/views/admin/procedures/_informations.html.haml +++ b/app/views/admin/procedures/_informations.html.haml @@ -45,7 +45,7 @@ .checkbox %label = ff.check_box :quartiers_prioritaires - Quartiers Prioritaires + Quartiers prioritaires %li .checkbox %label From 081ed9096867872e8971642a95284b082db6d9cd Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Thu, 29 Jun 2017 14:11:48 +0200 Subject: [PATCH 112/136] New Routes: add new_user/user_controller --- app/controllers/new_user/user_controller.rb | 5 +++++ spec/controllers/new_user/user_controller_spec.rb | 14 ++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 app/controllers/new_user/user_controller.rb create mode 100644 spec/controllers/new_user/user_controller_spec.rb diff --git a/app/controllers/new_user/user_controller.rb b/app/controllers/new_user/user_controller.rb new file mode 100644 index 000000000..5cd85ae0f --- /dev/null +++ b/app/controllers/new_user/user_controller.rb @@ -0,0 +1,5 @@ +module NewUser + class UserController < ApplicationController + before_action :authenticate_user! + end +end diff --git a/spec/controllers/new_user/user_controller_spec.rb b/spec/controllers/new_user/user_controller_spec.rb new file mode 100644 index 000000000..125466fb6 --- /dev/null +++ b/spec/controllers/new_user/user_controller_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe NewUser::UserController, type: :controller do + describe 'before actions: authenticate_gestionnaire!' do + it 'is present' do + before_actions = NewUser::UserController + ._process_action_callbacks + .find_all{|process_action_callbacks| process_action_callbacks.kind == :before} + .map(&:filter) + + expect(before_actions).to include(:authenticate_user!) + end + end +end From 15b16f36b784709d379c046eccad851083713289 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Thu, 29 Jun 2017 14:18:12 +0200 Subject: [PATCH 113/136] New Routes: add new_user/dossier_controller --- .../new_user/dossiers_controller.rb | 18 ++++++++ .../new_user/dossiers_controller_spec.rb | 46 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 app/controllers/new_user/dossiers_controller.rb create mode 100644 spec/controllers/new_user/dossiers_controller_spec.rb diff --git a/app/controllers/new_user/dossiers_controller.rb b/app/controllers/new_user/dossiers_controller.rb new file mode 100644 index 000000000..d53e1e9b8 --- /dev/null +++ b/app/controllers/new_user/dossiers_controller.rb @@ -0,0 +1,18 @@ +module NewUser + class DossiersController < UserController + before_action :ensure_ownership! + + private + + def dossier + Dossier.find(params[:dossier_id]) + end + + def ensure_ownership! + if dossier.user != current_user + flash[:alert] = "Vous n'avez pas accès à ce dossier" + redirect_to root_path + end + end + end +end diff --git a/spec/controllers/new_user/dossiers_controller_spec.rb b/spec/controllers/new_user/dossiers_controller_spec.rb new file mode 100644 index 000000000..70c6519e2 --- /dev/null +++ b/spec/controllers/new_user/dossiers_controller_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' + +describe NewUser::DossiersController, type: :controller do + let(:user) { create(:user) } + + describe 'before_action: ensure_ownership!' do + it 'is present' do + before_actions = NewUser::DossiersController + ._process_action_callbacks + .find_all{|process_action_callbacks| process_action_callbacks.kind == :before} + .map(&:filter) + + expect(before_actions).to include(:ensure_ownership!) + end + end + + describe 'ensure_ownership!' do + let(:user) { create(:user) } + + before do + @controller.params[:dossier_id] = asked_dossier.id + expect(@controller).to receive(:current_user).and_return(user) + allow(@controller).to receive(:redirect_to) + + @controller.send(:ensure_ownership!) + end + + context 'when a user asks for its dossier' do + let(:asked_dossier) { create(:dossier, user: user) } + + it 'does not redirects nor flash' do + expect(@controller).not_to have_received(:redirect_to) + expect(flash.alert).to eq(nil) + end + end + + context 'when a user asks for another dossier' do + let(:asked_dossier) { create(:dossier) } + + it 'redirects and flash' do + expect(@controller).to have_received(:redirect_to).with(root_path) + expect(flash.alert).to eq("Vous n'avez pas accès à ce dossier") + end + end + end +end From 1e661fa686b8a78111b81c7b041dc96962fce1ce Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Thu, 29 Jun 2017 14:18:59 +0200 Subject: [PATCH 114/136] New Routes: add new_user/dossier_controller attestation --- .../new_user/dossiers_controller.rb | 4 ++++ config/routes.rb | 6 ++++++ .../new_user/dossiers_controller_spec.rb | 21 +++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/app/controllers/new_user/dossiers_controller.rb b/app/controllers/new_user/dossiers_controller.rb index d53e1e9b8..bc7df485d 100644 --- a/app/controllers/new_user/dossiers_controller.rb +++ b/app/controllers/new_user/dossiers_controller.rb @@ -2,6 +2,10 @@ module NewUser class DossiersController < UserController before_action :ensure_ownership! + def attestation + send_data(dossier.attestation.pdf.read, filename: 'attestation.pdf', type: 'application/pdf') + end + private def dossier diff --git a/config/routes.rb b/config/routes.rb index 8f58cce09..bfb706df8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -229,5 +229,11 @@ Rails.application.routes.draw do get "patron" => "root#patron" + scope module: 'new_user' do + resources :dossiers, only: [] do + get 'attestation' + end + end + apipie end diff --git a/spec/controllers/new_user/dossiers_controller_spec.rb b/spec/controllers/new_user/dossiers_controller_spec.rb index 70c6519e2..b08d7d21d 100644 --- a/spec/controllers/new_user/dossiers_controller_spec.rb +++ b/spec/controllers/new_user/dossiers_controller_spec.rb @@ -43,4 +43,25 @@ describe NewUser::DossiersController, type: :controller do end end end + + describe 'attestation' do + before { sign_in(user) } + + context 'when a dossier has an attestation' do + let(:fake_pdf) { double(read: 'pdf content') } + let!(:dossier) { create(:dossier, attestation: Attestation.new, user: user) } + + it 'returns the attestation pdf' do + allow_any_instance_of(Attestation).to receive(:pdf).and_return(fake_pdf) + + expect(controller).to receive(:send_data) + .with('pdf content', filename: 'attestation.pdf', type: 'application/pdf') do + controller.render nothing: true + end + + get :attestation, params: { dossier_id: dossier.id } + expect(response).to have_http_status(:success) + end + end + end end From 3eec07ac08036d0c28e290fcd6783a2abeee3929 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Thu, 29 Jun 2017 15:29:48 +0200 Subject: [PATCH 115/136] New Routes: add new_gestionnaire/gestionnaire_controller --- .../new_gestionnaire/gestionnaire_controller.rb | 5 +++++ .../gestionnaire_controller_spec.rb | 15 +++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 app/controllers/new_gestionnaire/gestionnaire_controller.rb create mode 100644 spec/controllers/new_gestionnaire/gestionnaire_controller_spec.rb diff --git a/app/controllers/new_gestionnaire/gestionnaire_controller.rb b/app/controllers/new_gestionnaire/gestionnaire_controller.rb new file mode 100644 index 000000000..76ef0b937 --- /dev/null +++ b/app/controllers/new_gestionnaire/gestionnaire_controller.rb @@ -0,0 +1,5 @@ +module NewGestionnaire + class GestionnaireController < ApplicationController + before_action :authenticate_gestionnaire! + end +end diff --git a/spec/controllers/new_gestionnaire/gestionnaire_controller_spec.rb b/spec/controllers/new_gestionnaire/gestionnaire_controller_spec.rb new file mode 100644 index 000000000..ed8ac8fba --- /dev/null +++ b/spec/controllers/new_gestionnaire/gestionnaire_controller_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe NewGestionnaire::GestionnaireController, type: :controller do + describe 'before actions: authenticate_gestionnaire!' do + it 'is present' do + before_actions = NewGestionnaire::GestionnaireController + ._process_action_callbacks + .find_all{|process_action_callbacks| process_action_callbacks.kind == :before} + .map(&:filter) + + expect(before_actions).to include(:authenticate_gestionnaire!) + end + end +end + From 94f8fb748fceb2495741c6ab6e496c5363a649b5 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Thu, 29 Jun 2017 15:30:42 +0200 Subject: [PATCH 116/136] New Routes: add new_gestionnaire/procedures_controller --- .../new_gestionnaire/procedures_controller.rb | 18 ++++++++ .../procedures_controller_spec.rb | 44 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 app/controllers/new_gestionnaire/procedures_controller.rb create mode 100644 spec/controllers/new_gestionnaire/procedures_controller_spec.rb diff --git a/app/controllers/new_gestionnaire/procedures_controller.rb b/app/controllers/new_gestionnaire/procedures_controller.rb new file mode 100644 index 000000000..bfffad1fa --- /dev/null +++ b/app/controllers/new_gestionnaire/procedures_controller.rb @@ -0,0 +1,18 @@ +module NewGestionnaire + class ProceduresController < GestionnaireController + before_action :ensure_ownership! + + private + + def procedure + Procedure.find(params[:procedure_id]) + end + + def ensure_ownership! + if !procedure.gestionnaires.include?(current_gestionnaire) + flash[:alert] = "Vous n'avez pas accès à cette procédure" + redirect_to root_path + end + end + end +end diff --git a/spec/controllers/new_gestionnaire/procedures_controller_spec.rb b/spec/controllers/new_gestionnaire/procedures_controller_spec.rb new file mode 100644 index 000000000..fc7623488 --- /dev/null +++ b/spec/controllers/new_gestionnaire/procedures_controller_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +describe NewGestionnaire::ProceduresController, type: :controller do + describe 'before_action: ensure_ownership!' do + it 'is present' do + before_actions = NewGestionnaire::ProceduresController + ._process_action_callbacks + .find_all{|process_action_callbacks| process_action_callbacks.kind == :before} + .map(&:filter) + + expect(before_actions).to include(:ensure_ownership!) + end + end + + describe 'ensure_ownership!' do + let(:gestionnaire) { create(:gestionnaire) } + + before do + @controller.params[:procedure_id] = asked_procedure.id + expect(@controller).to receive(:current_gestionnaire).and_return(gestionnaire) + allow(@controller).to receive(:redirect_to) + + @controller.send(:ensure_ownership!) + end + + context 'when a gestionnaire asks for its procedure' do + let(:asked_procedure) { create(:procedure, gestionnaires: [gestionnaire]) } + + it 'does not redirects nor flash' do + expect(@controller).not_to have_received(:redirect_to) + expect(flash.alert).to eq(nil) + end + end + + context 'when a gestionnaire asks for another procedure' do + let(:asked_procedure) { create(:procedure) } + + it 'redirects and flash' do + expect(@controller).to have_received(:redirect_to).with(root_path) + expect(flash.alert).to eq("Vous n'avez pas accès à cette procédure") + end + end + end +end From 4e6a8c59397e78c2d48da4241be1b0c7bdaa23c7 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Thu, 29 Jun 2017 15:31:29 +0200 Subject: [PATCH 117/136] New Routes: add new_gestionnaire/dossier_controller attestation --- .../new_gestionnaire/dossiers_controller.rb | 13 ++++++++++ config/routes.rb | 8 ++++++ .../dossiers_controller_spec.rb | 26 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 app/controllers/new_gestionnaire/dossiers_controller.rb create mode 100644 spec/controllers/new_gestionnaire/dossiers_controller_spec.rb diff --git a/app/controllers/new_gestionnaire/dossiers_controller.rb b/app/controllers/new_gestionnaire/dossiers_controller.rb new file mode 100644 index 000000000..417e90ea8 --- /dev/null +++ b/app/controllers/new_gestionnaire/dossiers_controller.rb @@ -0,0 +1,13 @@ +module NewGestionnaire + class DossiersController < ProceduresController + def attestation + send_data(dossier.attestation.pdf.read, filename: 'attestation.pdf', type: 'application/pdf') + end + + private + + def dossier + Dossier.find(params[:dossier_id]) + end + end +end diff --git a/config/routes.rb b/config/routes.rb index bfb706df8..32ced0aae 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -235,5 +235,13 @@ Rails.application.routes.draw do end end + scope module: 'new_gestionnaire' do + resources :procedures, only: [] do + resources :dossiers, only: [] do + get 'attestation' + end + end + end + apipie end diff --git a/spec/controllers/new_gestionnaire/dossiers_controller_spec.rb b/spec/controllers/new_gestionnaire/dossiers_controller_spec.rb new file mode 100644 index 000000000..0ec7b4bda --- /dev/null +++ b/spec/controllers/new_gestionnaire/dossiers_controller_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe NewGestionnaire::DossiersController, type: :controller do + let(:gestionnaire) { create(:gestionnaire) } + before { sign_in(gestionnaire) } + + describe 'attestation' do + context 'when a dossier has an attestation' do + let(:fake_pdf) { double(read: 'pdf content') } + let!(:procedure) { create(:procedure, gestionnaires: [gestionnaire]) } + let!(:dossier) { create(:dossier, attestation: Attestation.new, procedure: procedure) } + + it 'returns the attestation pdf' do + allow_any_instance_of(Attestation).to receive(:pdf).and_return(fake_pdf) + + expect(controller).to receive(:send_data) + .with('pdf content', filename: 'attestation.pdf', type: 'application/pdf') do + controller.render nothing: true + end + + get :attestation, params: { procedure_id: procedure.id, dossier_id: dossier.id } + expect(response).to have_http_status(:success) + end + end + end +end From b1d96f4a941921c59ef5a271acd06403c4cf0ca2 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Thu, 29 Jun 2017 15:42:13 +0200 Subject: [PATCH 118/136] [FIX #492] switch to new attestation download system --- .../users/recapitulatif_controller.rb | 4 ---- app/views/dossiers/_attestation.html.haml | 5 ++++- config/routes.rb | 2 -- .../users/recapitulatif_controller_spec.rb | 18 ------------------ 4 files changed, 4 insertions(+), 25 deletions(-) diff --git a/app/controllers/users/recapitulatif_controller.rb b/app/controllers/users/recapitulatif_controller.rb index 9d470d500..88dd4d2f8 100644 --- a/app/controllers/users/recapitulatif_controller.rb +++ b/app/controllers/users/recapitulatif_controller.rb @@ -7,10 +7,6 @@ class Users::RecapitulatifController < UsersController create_dossier_facade end - def attestation - send_data(current_user_dossier.attestation.pdf.read, filename: 'attestation.pdf', type: 'application/pdf') - end - def initiate create_dossier_facade diff --git a/app/views/dossiers/_attestation.html.haml b/app/views/dossiers/_attestation.html.haml index b6c4dc3ce..dc77bc5f2 100644 --- a/app/views/dossiers/_attestation.html.haml +++ b/app/views/dossiers/_attestation.html.haml @@ -11,4 +11,7 @@ = image_tag('pdf.svg', width: '20px') %p.title= dossier.attestation.title %p.delivery Délivrée le #{l(dossier.attestation.created_at, format: '%d %B %Y')} - %a.btn.btn-primary{ href: users_dossier_recapitulatif_attestation_path(dossier), target: '_blank' } Télécharger + - if user_signed_in? + = link_to 'Télécharger', dossier_attestation_path(dossier), target: '_blank', class: 'btn btn-primary' + - else + = link_to 'Télécharger', procedure_dossier_attestation_path(dossier.procedure, dossier), target: '_blank', class: 'btn btn-primary' diff --git a/config/routes.rb b/config/routes.rb index 32ced0aae..c67ba0dc9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -82,8 +82,6 @@ Rails.application.routes.draw do get '/recapitulatif' => 'recapitulatif#show' post '/recapitulatif/initiate' => 'recapitulatif#initiate' - get '/recapitulatif/attestation' => 'recapitulatif#attestation' - post '/commentaire' => 'commentaires#create' resources :commentaires, only: [:index] diff --git a/spec/controllers/users/recapitulatif_controller_spec.rb b/spec/controllers/users/recapitulatif_controller_spec.rb index 326699f3e..120e2e344 100644 --- a/spec/controllers/users/recapitulatif_controller_spec.rb +++ b/spec/controllers/users/recapitulatif_controller_spec.rb @@ -51,22 +51,4 @@ describe Users::RecapitulatifController, type: :controller do end end end - - describe 'Get #attestation' do - context 'when a dossier has an attestation' do - let(:fake_pdf) { double(read: 'pdf content') } - let!(:dossier) { create(:dossier, attestation: Attestation.new) } - - it 'returns the attestation pdf' do - allow_any_instance_of(Attestation).to receive(:pdf).and_return(fake_pdf) - - expect(controller).to receive(:send_data) - .with('pdf content', filename: 'attestation.pdf', type: 'application/pdf') do - controller.render nothing: true - end - - get :attestation, params: { dossier_id: dossier.id } - end - end - end end From 649af194435f8bf24bf646f930c96bc21ceeb7ee Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Thu, 22 Jun 2017 14:56:59 +0200 Subject: [PATCH 119/136] Add search field in navbar --- app/assets/images/icons/search-blue.svg | 1 + .../stylesheets/new_design/new_header.scss | 35 ++++++++++++++++++- app/views/layouts/_new_header.haml | 16 +++++++-- 3 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 app/assets/images/icons/search-blue.svg diff --git a/app/assets/images/icons/search-blue.svg b/app/assets/images/icons/search-blue.svg new file mode 100644 index 000000000..e1b35abf2 --- /dev/null +++ b/app/assets/images/icons/search-blue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/stylesheets/new_design/new_header.scss b/app/assets/stylesheets/new_design/new_header.scss index aa542550e..2584e62b1 100644 --- a/app/assets/stylesheets/new_design/new_header.scss +++ b/app/assets/stylesheets/new_design/new_header.scss @@ -23,6 +23,40 @@ margin-top: 17px; } +.header-right-content { + display: flex; + align-items: center; + height: 100%; + + li { + @include horizontal-padding(8px); + + &:last-child { + padding-right: 0; + } + } +} + +.header-search { + position: relative; + + .form input[type=text] { + padding: 9px; + padding-right: 42px; + float: right; + width: 300px; + } + + button { + padding: 6px 9px; + border: none; + background: none; + cursor: pointer; + position: absolute; + right: 0; + } +} + $header-login-button-height: 36px; $header-login-button-border-size: 1px; @@ -36,7 +70,6 @@ $header-login-button-border-size: 1px; border: $header-login-button-border-size solid $blue; color: $blue; font-size: 14px; - margin-top: 18px; &:hover { color: #FFFFFF; diff --git a/app/views/layouts/_new_header.haml b/app/views/layouts/_new_header.haml index fc6e50e9e..6e3e1c545 100644 --- a/app/views/layouts/_new_header.haml +++ b/app/views/layouts/_new_header.haml @@ -1,7 +1,17 @@ -.new-header{ class: current_page?(root_path) ? nil : 'new-header-with-border' } +.new-header{ class: current_page?(root_path) ? nil : "new-header-with-border" } .header-inner-content = link_to root_path do %img.header-logo{ src: image_url("header/logo-tps.svg") } - - unless request.path == new_user_session_path - = link_to "Connexion", new_user_session_path, class: "header-login-button" + %ul.header-right-content + - if gestionnaire_signed_in? + %li + .header-search + = form_tag backoffice_dossiers_search_url, method: :get, class: "form" do + = text_field_tag "q", "#{@search_terms unless @search_terms.nil?}", placeholder: "Rechercher" + %button{ title: "Rechercher" } + = image_tag "icons/search-blue.svg" + + - elsif request.path != new_user_session_path + %li + = link_to "Connexion", new_user_session_path, class: "header-login-button" From b7dc511a7d61eee3988645231b13975f0e1bab0a Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Mon, 26 Jun 2017 12:01:40 +0200 Subject: [PATCH 120/136] Create a clean application.js file for new design --- .../javascripts/new_design/application.js | 18 ++++++++++++++++++ app/views/layouts/new_application.html.haml | 2 +- config/initializers/assets.rb | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 app/assets/javascripts/new_design/application.js diff --git a/app/assets/javascripts/new_design/application.js b/app/assets/javascripts/new_design/application.js new file mode 100644 index 000000000..700bc74fb --- /dev/null +++ b/app/assets/javascripts/new_design/application.js @@ -0,0 +1,18 @@ +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. +// +// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details +// about supported directives. +// +//= require jquery +//= require jquery_ujs +//= require turbolinks +//= require highcharts +//= require chartkick +//= require_tree . diff --git a/app/views/layouts/new_application.html.haml b/app/views/layouts/new_application.html.haml index b12804452..3e2196589 100644 --- a/app/views/layouts/new_application.html.haml +++ b/app/views/layouts/new_application.html.haml @@ -37,7 +37,7 @@ = render partial: "layouts/mailjet_newsletter" = render partial: "layouts/crisp" - = javascript_include_tag "application", "data-turbolinks-track": true + = javascript_include_tag "new_design/application", "data-turbolinks-track": true = yield :charts_js - if Rails.env == "test" %script{ type: "text/javascript" } diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index c20bc1259..f6ab56d75 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -8,4 +8,4 @@ Rails.application.config.assets.version = '1.0' # Precompile additional assets. # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. -Rails.application.config.assets.precompile += %w(print.css new_design/new_application.css) +Rails.application.config.assets.precompile += %w(print.css new_design/new_application.css new_design/application.js) From 33af09cae5488c1c357ba233fe84a1c2f51e4f38 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Thu, 22 Jun 2017 16:45:57 +0200 Subject: [PATCH 121/136] Account menu on header --- app/assets/images/icons/account-circle.svg | 1 + app/assets/images/icons/sign-out.svg | 1 + app/assets/javascripts/new_design/header.js | 10 +++ .../stylesheets/new_design/_placeholders.scss | 5 ++ .../stylesheets/new_design/animations.scss | 18 ++++++ .../stylesheets/new_design/new_header.scss | 64 ++++++++++++++++++- app/helpers/application_helper.rb | 6 ++ app/views/layouts/_new_header.haml | 11 ++++ 8 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 app/assets/images/icons/account-circle.svg create mode 100644 app/assets/images/icons/sign-out.svg create mode 100644 app/assets/javascripts/new_design/header.js create mode 100644 app/assets/stylesheets/new_design/animations.scss diff --git a/app/assets/images/icons/account-circle.svg b/app/assets/images/icons/account-circle.svg new file mode 100644 index 000000000..ec478f66c --- /dev/null +++ b/app/assets/images/icons/account-circle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/sign-out.svg b/app/assets/images/icons/sign-out.svg new file mode 100644 index 000000000..30022a12b --- /dev/null +++ b/app/assets/images/icons/sign-out.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/javascripts/new_design/header.js b/app/assets/javascripts/new_design/header.js new file mode 100644 index 000000000..903b0ff96 --- /dev/null +++ b/app/assets/javascripts/new_design/header.js @@ -0,0 +1,10 @@ +window.TPS = window.TPS || {}; + +$(document).on("click", "body", function () { + $(".header-menu").removeClass("open fade-in-down"); +}); + +TPS.toggleHeaderMenu = function(event) { + event.stopPropagation(); + $(".header-menu").toggleClass("open fade-in-down"); +} diff --git a/app/assets/stylesheets/new_design/_placeholders.scss b/app/assets/stylesheets/new_design/_placeholders.scss index fbb5bf9d5..086e5ecfe 100644 --- a/app/assets/stylesheets/new_design/_placeholders.scss +++ b/app/assets/stylesheets/new_design/_placeholders.scss @@ -19,3 +19,8 @@ max-width: $page-width + 2 * $default-padding; margin: 0 auto; } + +%animation { + animation-fill-mode: forwards; + animation-duration: 0.3s; +} diff --git a/app/assets/stylesheets/new_design/animations.scss b/app/assets/stylesheets/new_design/animations.scss new file mode 100644 index 000000000..c9df122d2 --- /dev/null +++ b/app/assets/stylesheets/new_design/animations.scss @@ -0,0 +1,18 @@ +@import "placeholders"; + +@keyframes fade-in-down { + 0% { + opacity: 0; + margin-top: -10px; + } + + 100% { + opacity: 1; + margin-top: 0; + } +} + +.fade-in-down { + @extend %animation; + animation-name: fade-in-down; +} diff --git a/app/assets/stylesheets/new_design/new_header.scss b/app/assets/stylesheets/new_design/new_header.scss index 2584e62b1..fbb063aa2 100644 --- a/app/assets/stylesheets/new_design/new_header.scss +++ b/app/assets/stylesheets/new_design/new_header.scss @@ -28,7 +28,7 @@ align-items: center; height: 100%; - li { + > li { @include horizontal-padding(8px); &:last-child { @@ -54,6 +54,68 @@ cursor: pointer; position: absolute; right: 0; + + &:hover { + opacity: 0.8; + } + } +} + +.header-menu-opener { + position: relative; + + img { + cursor: pointer; + + &:hover { + opacity: 0.8; + } + } +} + +.header-menu { + display: none; + position: absolute; + right: 0; + top: 34px; + font-size: 14px; + background: #FFFFFF; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); + border: 1px solid $border-grey; + min-width: 270px; + max-width: 340px; + + &.open { + display: block; + } + + li { + border-bottom: 1px solid $border-grey; + + &:last-child { + border-bottom: none; + } + + .menu-item { + align-items: center; + padding: 14px; + color: $grey; + overflow: hidden; + text-overflow: ellipsis; + + img { + margin-right: 14px; + } + } + + .menu-link { + display: flex; + color: $black; + + &:hover { + background: $light-grey; + } + } } } diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 8aed47dc2..f5572c1d9 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -5,4 +5,10 @@ module ApplicationHelper when "alert" then "alert-danger" end end + + def current_email + current_user.try(:email) || + current_gestionnaire.try(:email) || + current_administrateur.try(:email) + end end diff --git a/app/views/layouts/_new_header.haml b/app/views/layouts/_new_header.haml index 6e3e1c545..29df740c7 100644 --- a/app/views/layouts/_new_header.haml +++ b/app/views/layouts/_new_header.haml @@ -11,6 +11,17 @@ = text_field_tag "q", "#{@search_terms unless @search_terms.nil?}", placeholder: "Rechercher" %button{ title: "Rechercher" } = image_tag "icons/search-blue.svg" + %li + .header-menu-opener + = image_tag "icons/account-circle.svg", onclick: "javascript:TPS.toggleHeaderMenu(event);", title: "Mon compte" + %ul.header-menu + %li + .menu-item{ title: current_email } + = current_email + %li + = link_to destroy_user_session_path, method: :delete, class: "menu-item menu-link" do + = image_tag "icons/sign-out.svg" + Se déconnecter - elsif request.path != new_user_session_path %li From 5d8c7983690dc5b2e8893a2a66ccee5ad781dd4f Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Mon, 26 Jun 2017 11:40:28 +0200 Subject: [PATCH 122/136] Connexion button is a secondary button --- .../stylesheets/new_design/buttons.scss | 1 + .../stylesheets/new_design/new_header.scss | 30 ------------------- app/views/layouts/_new_header.haml | 2 +- app/views/root/patron.html.haml | 2 ++ 4 files changed, 4 insertions(+), 31 deletions(-) diff --git a/app/assets/stylesheets/new_design/buttons.scss b/app/assets/stylesheets/new_design/buttons.scss index 5e0bf38f3..bd4a512b8 100644 --- a/app/assets/stylesheets/new_design/buttons.scss +++ b/app/assets/stylesheets/new_design/buttons.scss @@ -31,6 +31,7 @@ background-color: #FFFFFF; &:hover { + color: #FFFFFF; background: $light-blue; } } diff --git a/app/assets/stylesheets/new_design/new_header.scss b/app/assets/stylesheets/new_design/new_header.scss index fbb063aa2..9767723ac 100644 --- a/app/assets/stylesheets/new_design/new_header.scss +++ b/app/assets/stylesheets/new_design/new_header.scss @@ -118,33 +118,3 @@ } } } - -$header-login-button-height: 36px; -$header-login-button-border-size: 1px; - -.header-login-button { - @include horizontal-padding(16px); - - display: inline-block; - height: $header-login-button-height; - line-height: $header-login-button-height - (2 * $header-login-button-border-size); - border-radius: $header-login-button-height; - border: $header-login-button-border-size solid $blue; - color: $blue; - font-size: 14px; - - &:hover { - color: #FFFFFF; - text-decoration: none; - background-color: $light-blue; - } - - &:focus { - color: $blue; - text-decoration: none; - } - - &:hover:focus { - color: #FFFFFF; - } -} diff --git a/app/views/layouts/_new_header.haml b/app/views/layouts/_new_header.haml index 29df740c7..492525750 100644 --- a/app/views/layouts/_new_header.haml +++ b/app/views/layouts/_new_header.haml @@ -25,4 +25,4 @@ - elsif request.path != new_user_session_path %li - = link_to "Connexion", new_user_session_path, class: "header-login-button" + = link_to "Connexion", new_user_session_path, class: "button secondary" diff --git a/app/views/root/patron.html.haml b/app/views/root/patron.html.haml index a219a9da9..6d2a06f92 100644 --- a/app/views/root/patron.html.haml +++ b/app/views/root/patron.html.haml @@ -20,6 +20,8 @@ = link_to ".button.primary", "#", class: "button primary" + = link_to ".button.secondary", "#", class: "button secondary" + = link_to ".button.large", "#", class: "button large" = link_to ".button.large.primary", "#", class: "button large primary" From dc4decc8275d3bf9c2668653d61ce4737542ffd9 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Mon, 26 Jun 2017 14:01:54 +0200 Subject: [PATCH 123/136] Add profile switcher in header --- app/assets/images/icons/switch-profile.svg | 1 + app/views/layouts/_new_header.haml | 18 ++++++++++++++++++ app/views/layouts/new_application.html.haml | 2 -- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 app/assets/images/icons/switch-profile.svg diff --git a/app/assets/images/icons/switch-profile.svg b/app/assets/images/icons/switch-profile.svg new file mode 100644 index 000000000..e5d63c13c --- /dev/null +++ b/app/assets/images/icons/switch-profile.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/views/layouts/_new_header.haml b/app/views/layouts/_new_header.haml index 492525750..5e590e187 100644 --- a/app/views/layouts/_new_header.haml +++ b/app/views/layouts/_new_header.haml @@ -18,6 +18,24 @@ %li .menu-item{ title: current_email } = current_email + + - if SwitchDeviseProfileService.new(warden).multiple_devise_profile_connect? + - if user_signed_in? + %li + = link_to users_dossiers_path, class: "menu-item menu-link" do + = image_tag "icons/switch-profile.svg" + Passer en usager + - if gestionnaire_signed_in? + %li + = link_to backoffice_dossiers_path, class: "menu-item menu-link" do + = image_tag "icons/switch-profile.svg" + Passer en accompagnateur + - if administrateur_signed_in? + %li + = link_to admin_procedures_path, class: "menu-item menu-link" do + = image_tag "icons/switch-profile.svg" + Passer en administrateur + %li = link_to destroy_user_session_path, method: :delete, class: "menu-item menu-link" do = image_tag "icons/sign-out.svg" diff --git a/app/views/layouts/new_application.html.haml b/app/views/layouts/new_application.html.haml index 3e2196589..799b9f413 100644 --- a/app/views/layouts/new_application.html.haml +++ b/app/views/layouts/new_application.html.haml @@ -30,8 +30,6 @@ = render partial: "layouts/flash_messages" = yield - = render partial: "layouts/switch_devise_profile_module" - = render partial: "layouts/new_footer" = render partial: "layouts/google_analytics" = render partial: "layouts/mailjet_newsletter" From 84751cdedfa6ef3de7a83f9b7c075e23efd5ad74 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 4 Jul 2017 12:05:11 +0200 Subject: [PATCH 124/136] [Fix #528] Fix a spelling mistake --- app/controllers/users_controller.rb | 2 +- spec/features/users/drawing_a_zone_with_freedraw_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 3f013040e..d2948b456 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -16,7 +16,7 @@ class UsersController < ApplicationController end def authorized_routes? controller - redirect_to_root_path 'Le status de votre dossier n\'autorise pas cette URL' unless UserRoutesAuthorizationService.authorized_route?( + redirect_to_root_path 'Le statut de votre dossier n\'autorise pas cette URL' unless UserRoutesAuthorizationService.authorized_route?( controller, current_user_dossier) rescue ActiveRecord::RecordNotFound diff --git a/spec/features/users/drawing_a_zone_with_freedraw_spec.rb b/spec/features/users/drawing_a_zone_with_freedraw_spec.rb index e11c7189b..a7e54ce25 100644 --- a/spec/features/users/drawing_a_zone_with_freedraw_spec.rb +++ b/spec/features/users/drawing_a_zone_with_freedraw_spec.rb @@ -49,7 +49,7 @@ feature 'drawing a zone with freedraw' do end scenario 'alert message is present' do - expect(page).to have_content('Le status de votre dossier n\'autorise pas cette URL') + expect(page).to have_content('Le statut de votre dossier n\'autorise pas cette URL') end end end From 1d4a0dce5692a9aae65870cc94606a458930e473 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Tue, 4 Jul 2017 12:33:43 +0200 Subject: [PATCH 125/136] Header: fix alignement in ff --- app/assets/stylesheets/new_design/new_header.scss | 6 +----- app/views/layouts/_new_header.haml | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/new_design/new_header.scss b/app/assets/stylesheets/new_design/new_header.scss index 9767723ac..9ea00b625 100644 --- a/app/assets/stylesheets/new_design/new_header.scss +++ b/app/assets/stylesheets/new_design/new_header.scss @@ -17,16 +17,12 @@ @extend %page-width-container; display: flex; justify-content: space-between; -} - -.header-logo { - margin-top: 17px; + padding-top: 17px; } .header-right-content { display: flex; align-items: center; - height: 100%; > li { @include horizontal-padding(8px); diff --git a/app/views/layouts/_new_header.haml b/app/views/layouts/_new_header.haml index 5e590e187..e6919dc50 100644 --- a/app/views/layouts/_new_header.haml +++ b/app/views/layouts/_new_header.haml @@ -1,7 +1,7 @@ .new-header{ class: current_page?(root_path) ? nil : "new-header-with-border" } .header-inner-content = link_to root_path do - %img.header-logo{ src: image_url("header/logo-tps.svg") } + %img{ src: image_url("header/logo-tps.svg") } %ul.header-right-content - if gestionnaire_signed_in? From 8aaf7df5793ea1f1795bf39fd67399892ef90fb9 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Fri, 30 Jun 2017 12:59:12 +0200 Subject: [PATCH 126/136] AttestationTemplate edit: id.nil => new_record? --- app/views/admin/attestation_templates/edit.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/attestation_templates/edit.html.haml b/app/views/admin/attestation_templates/edit.html.haml index 9b54d4def..29bf812d4 100644 --- a/app/views/admin/attestation_templates/edit.html.haml +++ b/app/views/admin/attestation_templates/edit.html.haml @@ -66,7 +66,7 @@ - if @attestation_template.activated %button.btn.btn-warning{ formaction: admin_procedure_attestation_template_disactivate_path } désactiver l'attestation - - if @attestation_template.id.nil? || !@attestation_template.activated + - if @attestation_template.new_record? || !@attestation_template.activated %button.btn.btn-success Activer l'attestation - else %button.btn.btn-success Enregistrer From 48e881f9ec5f8ab4fde9710457235da59431ce28 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Fri, 30 Jun 2017 12:59:53 +0200 Subject: [PATCH 127/136] [FIX #491] Attestation: allow administrateur to change attestation template on a published procedure --- .../admin/attestation_templates_controller.rb | 10 ---------- app/views/admin/attestation_templates/edit.html.haml | 5 +++-- ...panel_admin_procedurescontroller_navbar.html.haml | 8 ++------ config/routes.rb | 2 +- .../admin/attestation_templates_controller_spec.rb | 12 ------------ 5 files changed, 6 insertions(+), 31 deletions(-) diff --git a/app/controllers/admin/attestation_templates_controller.rb b/app/controllers/admin/attestation_templates_controller.rb index 0a6ea60a3..bc3bf0f40 100644 --- a/app/controllers/admin/attestation_templates_controller.rb +++ b/app/controllers/admin/attestation_templates_controller.rb @@ -1,16 +1,6 @@ class Admin::AttestationTemplatesController < AdminController before_action :retrieve_procedure - def show - attestation_template = @procedure.attestation_template - @logo = attestation_template.logo - @title = attestation_template.title - @body = attestation_template.body - @signature = attestation_template.signature - @footer = attestation_template.footer - @created_at = DateTime.now - end - def edit @attestation_template = @procedure.attestation_template || AttestationTemplate.new(procedure: @procedure) end diff --git a/app/views/admin/attestation_templates/edit.html.haml b/app/views/admin/attestation_templates/edit.html.haml index 29bf812d4..f5d790808 100644 --- a/app/views/admin/attestation_templates/edit.html.haml +++ b/app/views/admin/attestation_templates/edit.html.haml @@ -63,10 +63,11 @@ %button.btn.btn-primary{ formaction: admin_procedure_attestation_template_preview_path, formtarget: '_blank' } Prévisualiser .pull-right - - if @attestation_template.activated + - if @attestation_template.activated && !@procedure.locked? %button.btn.btn-warning{ formaction: admin_procedure_attestation_template_disactivate_path } désactiver l'attestation - if @attestation_template.new_record? || !@attestation_template.activated %button.btn.btn-success Activer l'attestation - else - %button.btn.btn-success Enregistrer + - save_data = @procedure.locked? ? { toggle: :tooltip, confirm: "Attention: les modifications n'affecteront pas les attestations déjà délivrées." } : nil + %button.btn.btn-success{ data: save_data } Enregistrer diff --git a/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml b/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml index 37e2fe106..22362a245 100644 --- a/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml +++ b/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml @@ -45,12 +45,8 @@ .procedure-list-element{ class: ('active' if active == 'Prévisualisation') } Prévisualisation - - if !@procedure.locked? - %a#onglet-attestation{ href: url_for(edit_admin_procedure_attestation_template_path(@procedure)) } - .procedure-list-element{ class: ('active' if active == 'Attestation') } Attestation - - elsif @procedure.attestation_template.present? - %a#onglet-attestation{ href: url_for(admin_procedure_attestation_template_path(@procedure, format: :pdf)), target: '_blank' } - .procedure-list-element Attestation + %a#onglet-attestation{ href: url_for(edit_admin_procedure_attestation_template_path(@procedure)) } + .procedure-list-element{ class: ('active' if active == 'Attestation') } Attestation .split-hr-left diff --git a/config/routes.rb b/config/routes.rb index c67ba0dc9..a64710865 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -144,7 +144,7 @@ Rails.application.routes.draw do resource :previsualisation, only: [:show] - resource :attestation_template, only: [:show, :edit, :update, :create] + resource :attestation_template, only: [:edit, :update, :create] post 'attestation_template/disactivate' => 'attestation_templates#disactivate' patch 'attestation_template/disactivate' => 'attestation_templates#disactivate' diff --git a/spec/controllers/admin/attestation_templates_controller_spec.rb b/spec/controllers/admin/attestation_templates_controller_spec.rb index 7e62dcf43..cd318832a 100644 --- a/spec/controllers/admin/attestation_templates_controller_spec.rb +++ b/spec/controllers/admin/attestation_templates_controller_spec.rb @@ -10,18 +10,6 @@ describe Admin::AttestationTemplatesController, type: :controller do Timecop.freeze(Time.now) end - describe 'GET #show' do - before { get :show, params: { procedure_id: procedure.id, format: :pdf } } - - it { expect(subject.status).to eq(200) } - it { expect(assigns[:title]).to eq(attestation_template.title) } - it { expect(assigns[:logo].read).to eq(attestation_template.logo.read) } - it { expect(assigns[:body]).to eq(attestation_template.body) } - it { expect(assigns[:signature].read).to eq(attestation_template.signature.read) } - it { expect(assigns[:footer]).to eq(attestation_template.footer) } - it { expect(assigns[:created_at]).to eq(DateTime.now) } - end - describe 'POST #preview' do let(:upload_params) { { title: 't', body: 'b', footer: 'f' } } From 4ee17e44dccac2f83ba2310bfd4215fe980f66a2 Mon Sep 17 00:00:00 2001 From: gregoirenovel Date: Tue, 4 Jul 2017 12:03:25 +0200 Subject: [PATCH 128/136] [Fix #529] Add missing JS file to new_design/application.js --- app/assets/javascripts/{ => new_design}/toggle_chart.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/assets/javascripts/{ => new_design}/toggle_chart.js (100%) diff --git a/app/assets/javascripts/toggle_chart.js b/app/assets/javascripts/new_design/toggle_chart.js similarity index 100% rename from app/assets/javascripts/toggle_chart.js rename to app/assets/javascripts/new_design/toggle_chart.js From 12c12953d6ee156a742494ce5e9e39f8d8515dab Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Wed, 28 Jun 2017 07:08:25 +0200 Subject: [PATCH 129/136] [FIX #433] Add Raven Metadata on the current user/gestionnaire/admin ... --- app/controllers/application_controller.rb | 22 +++++++ .../application_controller_spec.rb | 59 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 spec/controllers/application_controller_spec.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index afcbf7916..191d2bcf8 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -4,6 +4,7 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception before_action :check_browser before_action :load_navbar_left_pannel_partial_url + before_action :set_raven_context def default_url_options return {protocol: 'https'} if Rails.env.staging? || Rails.env.production? @@ -40,4 +41,25 @@ class ApplicationController < ActionController::Base redirect_to new_user_session_path end end + + private + + def set_raven_context + context = { ip_address: request.ip } + + logged_models = [ + current_user, + current_gestionnaire, + current_administrateur, + current_administration + ].compact + + context[:email] = logged_models.first&.email + context[:id] = logged_models.first&.id + + class_names = logged_models.map { |model| model.class.name } + context[:classes] = class_names.any? ? class_names.join(', ') : 'Guest' + + Raven.user_context(context) + end end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb new file mode 100644 index 000000000..9a05618a2 --- /dev/null +++ b/spec/controllers/application_controller_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' + +describe ApplicationController, type: :controller do + describe 'before_action: set_raven_context' do + it 'is present' do + before_actions = ApplicationController + ._process_action_callbacks + .find_all{|process_action_callbacks| process_action_callbacks.kind == :before} + .map(&:filter) + + expect(before_actions).to include(:set_raven_context) + end + end + + describe 'set_raven_context' do + let(:current_user) { nil } + let(:current_gestionnaire) { nil } + let(:current_administrateur) { nil } + let(:current_administration) { nil } + + before do + expect(@controller).to receive(:current_user).and_return(current_user) + expect(@controller).to receive(:current_gestionnaire).and_return(current_gestionnaire) + expect(@controller).to receive(:current_administrateur).and_return(current_administrateur) + expect(@controller).to receive(:current_administration).and_return(current_administration) + allow(Raven).to receive(:user_context) + + @controller.send(:set_raven_context) + end + + context 'when no one is logged in' do + it do + expect(Raven).to have_received(:user_context) + .with({ ip_address: '0.0.0.0', email: nil, id: nil, classes: 'Guest' }) + end + end + + context 'when a user is logged in' do + let(:current_user) { create(:user) } + + it do + expect(Raven).to have_received(:user_context) + .with({ ip_address: '0.0.0.0', email: current_user.email, id: current_user.id, classes: 'User' }) + end + end + + context 'when someone is logged as a user, gestionnaire, administrateur and administration' do + let(:current_user) { create(:user) } + let(:current_gestionnaire) { create(:gestionnaire) } + let(:current_administrateur) { create(:administrateur) } + let(:current_administration) { create(:administration) } + + it do + expect(Raven).to have_received(:user_context) + .with({ ip_address: '0.0.0.0', email: current_user.email, id: current_user.id, classes: 'User, Gestionnaire, Administrateur, Administration' }) + end + end + end +end From 760efcdad3d695f752b79e586dca69b345146546 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 27 Jun 2017 14:22:43 +0200 Subject: [PATCH 130/136] Add hidden_at on procedure --- app/models/procedure.rb | 5 +++++ ...170627120928_add_hidden_at_to_procedures.rb | 6 ++++++ db/schema.rb | 4 +++- spec/models/procedure_spec.rb | 18 ++++++++++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20170627120928_add_hidden_at_to_procedures.rb diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 8b6481708..202a37f97 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -31,12 +31,17 @@ class Procedure < ActiveRecord::Base mount_uploader :logo, ProcedureLogoUploader + default_scope { where(hidden_at: nil) } scope :not_archived, -> { where(archived: false) } scope :by_libelle, -> { order(libelle: :asc) } validates :libelle, presence: true, allow_blank: false, allow_nil: false validates :description, presence: true, allow_blank: false, allow_nil: false + def hide! + self.update_attributes(hidden_at: DateTime.now) + end + def path procedure_path.path unless procedure_path.nil? end diff --git a/db/migrate/20170627120928_add_hidden_at_to_procedures.rb b/db/migrate/20170627120928_add_hidden_at_to_procedures.rb new file mode 100644 index 000000000..a0c109597 --- /dev/null +++ b/db/migrate/20170627120928_add_hidden_at_to_procedures.rb @@ -0,0 +1,6 @@ +class AddHiddenAtToProcedures < ActiveRecord::Migration[5.0] + def change + add_column :procedures, :hidden_at, :datetime + add_index :procedures, :hidden_at + end +end diff --git a/db/schema.rb b/db/schema.rb index ad99eb400..42baae83d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -381,7 +381,9 @@ ActiveRecord::Schema.define(version: 20170627143701) do t.string "lien_notice" t.boolean "for_individual", default: false t.boolean "individual_with_siret", default: false - t.datetime "auto_archive_on" + t.date "auto_archive_on" + t.datetime "hidden_at" + t.index ["hidden_at"], name: "index_procedures_on_hidden_at", using: :btree t.datetime "published_at" t.datetime "archived_at" end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 9de535d08..fa0f82a95 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -302,4 +302,22 @@ describe Procedure do it { is_expected.to eq('a-long-libelle-with-accents-blabla-coucou-hello-un') } end + + describe ".default_scope" do + let!(:procedure) { create(:procedure, hidden_at: hidden_at) } + + context "when hidden_at is nil" do + let(:hidden_at) { nil } + + it { expect(Procedure.count).to eq(1) } + it { expect(Procedure.all).to include(procedure) } + end + + context "when hidden_at is not nil" do + let(:hidden_at) { 2.days.ago } + + it { expect(Procedure.count).to eq(0) } + it { expect { Procedure.find(procedure.id) }.to raise_error(ActiveRecord::RecordNotFound) } + end + end end From be3304f71e31083d7064ee69524e96297fea6ea6 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 27 Jun 2017 15:26:40 +0200 Subject: [PATCH 131/136] Add a scope to Dossier to check if procedure is not hidden --- app/models/dossier.rb | 1 + app/models/procedure.rb | 4 +++- ...20170627144046_add_hidden_at_to_dossiers.rb | 6 ++++++ db/schema.rb | 4 +++- spec/models/dossier_spec.rb | 17 +++++++++++++++++ spec/models/procedure_spec.rb | 18 ++++++++++++++++++ 6 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20170627144046_add_hidden_at_to_dossiers.rb diff --git a/app/models/dossier.rb b/app/models/dossier.rb index fd5d23838..a430dbf23 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -42,6 +42,7 @@ class Dossier < ActiveRecord::Base belongs_to :procedure belongs_to :user + default_scope { where(hidden_at: nil) } scope :state_brouillon, -> { where(state: BROUILLON) } scope :state_not_brouillon, -> { where.not(state: BROUILLON) } scope :state_nouveaux, -> { where(state: NOUVEAUX) } diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 202a37f97..83e88b823 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -39,7 +39,9 @@ class Procedure < ActiveRecord::Base validates :description, presence: true, allow_blank: false, allow_nil: false def hide! - self.update_attributes(hidden_at: DateTime.now) + now = DateTime.now + self.update_attributes(hidden_at: now) + self.dossiers.update_all(hidden_at: now) end def path diff --git a/db/migrate/20170627144046_add_hidden_at_to_dossiers.rb b/db/migrate/20170627144046_add_hidden_at_to_dossiers.rb new file mode 100644 index 000000000..b70d02e69 --- /dev/null +++ b/db/migrate/20170627144046_add_hidden_at_to_dossiers.rb @@ -0,0 +1,6 @@ +class AddHiddenAtToDossiers < ActiveRecord::Migration[5.0] + def change + add_column :dossiers, :hidden_at, :datetime + add_index :dossiers, :hidden_at + end +end diff --git a/db/schema.rb b/db/schema.rb index 42baae83d..2b3bbab8c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170627143701) do +ActiveRecord::Schema.define(version: 20170627144046) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -175,6 +175,8 @@ ActiveRecord::Schema.define(version: 20170627143701) do t.datetime "received_at" t.datetime "processed_at" t.text "motivation" + t.datetime "hidden_at" + t.index ["hidden_at"], name: "index_dossiers_on_hidden_at", using: :btree t.index ["procedure_id"], name: "index_dossiers_on_procedure_id", using: :btree t.index ["user_id"], name: "index_dossiers_on_user_id", using: :btree end diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index 21d4c6c4c..39a6e6794 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -928,4 +928,21 @@ describe Dossier do end end end + + describe ".default_scope" do + let!(:dossier) { create(:dossier, hidden_at: hidden_at) } + + context "when dossier is not hidden" do + let(:hidden_at) { nil } + + it { expect(Dossier.count).to eq(1) } + it { expect(Dossier.all).to include(dossier) } + end + + context "when dossier is hidden" do + let(:hidden_at) { 1.day.ago } + + it { expect(Dossier.count).to eq(0) } + end + end end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index fa0f82a95..6e2a6124f 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -320,4 +320,22 @@ describe Procedure do it { expect { Procedure.find(procedure.id) }.to raise_error(ActiveRecord::RecordNotFound) } end end + + describe "#hide!" do + let(:procedure) { create(:procedure) } + let!(:dossier) { create(:dossier, procedure: procedure) } + let!(:dossier2) { create(:dossier, procedure: procedure) } + + it { expect(Dossier.count).to eq(2) } + it { expect(Dossier.all).to include(dossier, dossier2) } + + context "when hidding procedure" do + before do + procedure.hide! + end + + it { expect(procedure.dossiers.count).to eq(0) } + it { expect(Dossier.count).to eq(0) } + end + end end From 70c7c42e1fbd7d69f31e51de94dc92d3c31de73c Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Wed, 28 Jun 2017 18:18:51 +0200 Subject: [PATCH 132/136] [Fix #430] Add delete procedure button --- app/controllers/admin/procedures_controller.rb | 8 ++++++++ app/views/admin/procedures/show.html.haml | 17 +++++++++++++++++ config/routes.rb | 4 ++++ 3 files changed, 29 insertions(+) diff --git a/app/controllers/admin/procedures_controller.rb b/app/controllers/admin/procedures_controller.rb index df49606fe..5f7731658 100644 --- a/app/controllers/admin/procedures_controller.rb +++ b/app/controllers/admin/procedures_controller.rb @@ -42,6 +42,14 @@ class Admin::ProceduresController < AdminController def edit end + def hide + procedure = Procedure.find(params[:id]) + procedure.hide! + + flash.notice = "Procédure supprimée, en cas d'erreur contactez nous : contact@tps.apientreprise.fr" + redirect_to admin_procedures_draft_path + end + def destroy procedure = Procedure.find(params[:id]) diff --git a/app/views/admin/procedures/show.html.haml b/app/views/admin/procedures/show.html.haml index 10d45504e..91fa3dc42 100644 --- a/app/views/admin/procedures/show.html.haml +++ b/app/views/admin/procedures/show.html.haml @@ -129,3 +129,20 @@ Aucune statistique pour le moment - else = pie_chart @facade.dossiers_for_pie_highchart + + - if @facade.procedure.published? || @facade.procedure.archived? + %h3 Supprimer la procédure + .alert.alert-danger + %p + Attention : la suppression d'une procédure est définitive. + - dossiers_count = @facade.procedure.dossiers.count + - if dossiers_count > 0 + %p + = pluralize(dossiers_count, "dossier est rattaché", "dossiers sont rattachés") + à cette procédure, la suppression de cette procédure entrainera également leur suppression. + %p.text-right + = link_to "J'ai compris, je supprime la procédure", + hide_admin_procedure_path(@facade.procedure), + method: :post, + class: "btn btn-danger", + data: { confirm: "Voulez-vous supprimer la procédure ?", disable_with: "Suppression..." } diff --git a/config/routes.rb b/config/routes.rb index a64710865..b93f66f2b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -114,6 +114,10 @@ Rails.application.routes.draw do patch 'change_dossier_state' => 'change_dossier_state#change' resources :procedures do + member do + post :hide + end + resources :types_de_champ, only: [:destroy] resource :types_de_champ, only: [:show, :update] do post '/:index/move_up' => 'types_de_champ#move_up', as: :move_up From ce171208dad3a629b93df8600e5258e8c580bb78 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Thu, 29 Jun 2017 11:45:30 +0200 Subject: [PATCH 133/136] Fix redirect loop for gestionnaire if hidden procedure is saved in procedure_filter --- app/models/gestionnaire.rb | 10 +++++++--- spec/models/gestionnaire_spec.rb | 9 +++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/models/gestionnaire.rb b/app/models/gestionnaire.rb index c2252d18a..30185d0a2 100644 --- a/app/models/gestionnaire.rb +++ b/app/models/gestionnaire.rb @@ -20,9 +20,13 @@ class Gestionnaire < ActiveRecord::Base include CredentialsSyncableConcern def procedure_filter - return nil unless assign_to.pluck(:procedure_id).include?(self[:procedure_filter]) - - self[:procedure_filter] + procedure_id = self[:procedure_filter] + if procedures.find_by(id: procedure_id).present? + procedure_id + else + self.update_column(:procedure_filter, nil) + nil + end end def can_view_dossier?(dossier_id) diff --git a/spec/models/gestionnaire_spec.rb b/spec/models/gestionnaire_spec.rb index b55a26684..ba09804f2 100644 --- a/spec/models/gestionnaire_spec.rb +++ b/spec/models/gestionnaire_spec.rb @@ -260,6 +260,15 @@ describe Gestionnaire, type: :model do it { expect(AssignTo.where(gestionnaire: gestionnaire, procedure: procedure_3).count).to eq 0 } it { is_expected.to be_nil } end + + context "when procedure is hidden clear procedure_filter" do + before do + gestionnaire.update_column :procedure_filter, procedure_3.id + procedure_3.hide! + end + + it { is_expected.to be_nil } + end end end From 82d8871d03c857e3f58e1607753af6c60bfdbeee Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Tue, 4 Jul 2017 16:37:29 +0200 Subject: [PATCH 134/136] DossierShow: fix unattended bug on @champs_private --- app/views/dossiers/_dossier_show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/dossiers/_dossier_show.html.haml b/app/views/dossiers/_dossier_show.html.haml index d5d0ad5f4..afb5a5c77 100644 --- a/app/views/dossiers/_dossier_show.html.haml +++ b/app/views/dossiers/_dossier_show.html.haml @@ -57,7 +57,7 @@ = render partial: '/users/carte/map', locals: { dossier: @facade.dossier } = render partial: 'users/carte/init_carto', locals: { dossier: @facade.dossier } -- if @current_gestionnaire && gestionnaire_signed_in? && current_gestionnaire.assigned_on_procedure?(@facade.dossier.procedure_id) && @champs_private.count > 0 +- if @current_gestionnaire && gestionnaire_signed_in? && current_gestionnaire.assigned_on_procedure?(@facade.dossier.procedure_id) && @champs_private.present? .default-data-block.default_visible .row.show-block#private-fields .header From e51e5afe5538950e878e68bbf0d8e1c10c51fdc3 Mon Sep 17 00:00:00 2001 From: Mathieu Magnin Date: Tue, 4 Jul 2017 15:36:35 +0200 Subject: [PATCH 135/136] Fix tests that often fail on circle ci when comparing datetimes --- spec/models/procedure_spec.rb | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 6e2a6124f..cf31199ae 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -225,32 +225,42 @@ describe Procedure do describe '#publish!' do let(:procedure) { create(:procedure) } + let(:now) { Time.now.beginning_of_minute } before do - Timecop.freeze(Time.now) + Timecop.freeze(now) procedure.publish!("example-path") end it { expect(procedure.published).to eq(true) } it { expect(procedure.archived).to eq(false) } - it { expect(procedure.published_at).to eq(Time.now) } + it { expect(procedure.published_at).to eq(now) } it { expect(ProcedurePath.find_by_path("example-path")).to be } it { expect(ProcedurePath.find_by_path("example-path").procedure).to eq(procedure) } it { expect(ProcedurePath.find_by_path("example-path").administrateur).to eq(procedure.administrateur) } + + after do + Timecop.return + end end describe 'archive' do let(:procedure) { create(:procedure, :published) } let(:procedure_path) { ProcedurePath.find(procedure.procedure_path.id) } + let(:now) { Time.now.beginning_of_minute } before do - Timecop.freeze(Time.now) + Timecop.freeze(now) procedure.archive procedure.reload end it { expect(procedure.published).to be_truthy } it { expect(procedure.archived).to be_truthy } - it { expect(procedure.archived_at).to eq(Time.now) } + it { expect(procedure.archived_at).to eq(now) } + + after do + Timecop.return + end end describe 'total_dossier' do From 4fad288a8a5eb85a0f14888bef8bb5ba0782f2d0 Mon Sep 17 00:00:00 2001 From: Simon Lehericey Date: Tue, 4 Jul 2017 17:28:59 +0200 Subject: [PATCH 136/136] lastWeekOverview: fix template --- app/views/gestionnaire_mailer/last_week_overview.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/gestionnaire_mailer/last_week_overview.html.haml b/app/views/gestionnaire_mailer/last_week_overview.html.haml index 8dab014f9..892e5b257 100644 --- a/app/views/gestionnaire_mailer/last_week_overview.html.haml +++ b/app/views/gestionnaire_mailer/last_week_overview.html.haml @@ -36,7 +36,7 @@ dont #{procedure_overview.old_dossiers_en_instruction.count} depuis plus de 7 jours - if procedure_overview.old_dossiers_en_instruction.count < 6 \: - = procedure_overview.old_dossiers_en_instruction.each do |old_dossier| + = procedure_overview.old_dossiers_en_instruction.map do |old_dossier| - link_to "nº #{old_dossier.id}", backoffice_dossier_url(old_dossier), style: 'color: #4393F3;' - end.join(', ').html_safe